# -*- coding:utf-8 -*-
"""
Author: Zhao Yang
升级版多算法彩票预测应用
支持LSTM-CRF和多种新算法
"""
import sys
import os
import io
import subprocess
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QVBoxLayout, QPushButton,
    QLabel, QComboBox, QWidget, QTextEdit, QSpinBox, QHBoxLayout,
    QGroupBox, QCheckBox, QTabWidget, QTableWidget, QTableWidgetItem
)
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from datetime import datetime

# 检查依赖
TORCH_AVAILABLE = False
PANDAS_AVAILABLE = False

try:
    import torch
    from torch import nn
    from torchcrf import CRF
    TORCH_AVAILABLE = True
except ImportError:
    pass

try:
    import pandas as pd
    PANDAS_AVAILABLE = True
except ImportError:
    pass

try:
    import joblib
    import numpy as np
    from sklearn.preprocessing import MinMaxScaler
except ImportError:
    import numpy as np

if TORCH_AVAILABLE:
    from model import LstmCRFModel  # 导入模型类

# 设置标准输出和错误输出编码为utf-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")

# 设置控制台编码（Windows特定）
if sys.platform.startswith('win'):
    try:
        import locale
        locale.setlocale(locale.LC_ALL, 'zh_CN.UTF-8')
    except:
        pass

# ---------------- 配置 ----------------
name_path = {
    "dlt": {
        "name": "大乐透",
        "path": "./scripts/dlt/",
        "model_file": "dlt_model.pth",
        "scaler_X_file": "scaler_X.pkl",
        "lstm_train_script": "train_dlt_model.py",
        "random_train_script": "random_dlt_model.py",
        "fetch_script": "fetch_dlt_data.py"
    },
    "ssq": {
        "name": "双色球",
        "path": "./scripts/ssq/",
        "model_file": "ssq_model.pth",
        "scaler_X_file": "scaler_X.pkl",
        "lstm_train_script": "train_ssq_model.py",
        "random_train_script": "random_ssq_model.py",
        "fetch_script": "fetch_ssq_data.py"
    },
    "plw": {
        "name": "排列5",
        "path": "./scripts/plw/",
        "model_file": "plw_model.pth",
        "scaler_X_file": "scaler_X.pkl",
        "lstm_train_script": "train_plw_model.py",
        "random_train_script": "random_plw_model.py",
        "fetch_script": "fetch_plw_data.py"
    },
    "kl8": {
        "name": "快乐8",
        "path": "./scripts/kl8/",
        "model_file": "kl8_lstm_crf_model.pth",
        "scaler_X_file": "kl8_scaler.pkl",
        "lstm_train_script": "train_kl8_model.py",
        "random_train_script": "random_kl8_model.py",
        "fetch_script": "fetch_kl8_data.py"
    },
    "fc3d": {
        "name": "fc3d",
        "path": "./scripts/fc3d/",
        "model_file": "fc3d_model.pth",
        "scaler_X_file": "scaler_X.pkl",
        "lstm_train_script": "train_3d_model.py",
        "random_train_script": "random_3d_model.py",
        "fetch_script": "fetch_3d_data.py",
        "sequence_lstm_model_file": "fc3d_sequence_lstm_model.pth"
    }
}

class PLWPredictor:
    """专门处理排列5数据的预测器"""
    def __init__(self, csv_file, window_size):
        self.data = pd.read_csv(csv_file)
        self.scaler_X = MinMaxScaler()
        self.features, self.labels = self.preprocess(self.data, window_size)

    def preprocess(self, data, window_size):
        features, labels = [], []
        
        # 检查数据格式
        if 'draw_numbers' not in data.columns:
            raise ValueError("数据文件缺少 'draw_numbers' 列")
        
        # 解析draw_numbers列，提取5个数字
        number_data = []
        for _, row in data.iterrows():
            numbers_str = row['draw_numbers']
            if isinstance(numbers_str, str):
                # 去除引号并按逗号分割
                numbers = numbers_str.strip('"').split(',')
                # 取前5个数字（排列5格式）
                if len(numbers) >= 5:
                    # 对于非标准数据，使用模运算转换为0-9范围
                    parsed_numbers = [int(num.strip()) % 10 for num in numbers[:5]]
                    number_data.append(parsed_numbers)
                else:
                    # 如果不足5个数字，用0填充
                    parsed_numbers = [int(num.strip()) % 10 for num in numbers] + [0] * (5 - len(numbers))
                    number_data.append(parsed_numbers)
        
        number_data = np.array(number_data)
        
        # 创建时间窗口特征和标签
        for i in range(len(number_data) - window_size):
            # 特征：窗口内的5个数字
            feature_window = number_data[i:i + window_size]  # shape: (window_size, 5)
            features.append(feature_window)

            # 标签：下一期的5个数字
            next_numbers = number_data[i + window_size]  # shape: (5,)
            labels.append(next_numbers)

        # 转换为 NumPy 数组
        features_np = np.array(features)  # shape: (num_samples, window_size, 5)
        labels_np = np.array(labels)      # shape: (num_samples, 5)
        
        # 对特征进行缩放
        original_shape = features_np.shape
        features_scaled = self.scaler_X.fit_transform(
            features_np.reshape(-1, features_np.shape[-1])
        ).reshape(original_shape)

        return (
            torch.tensor(features_scaled, dtype=torch.float32),
            torch.tensor(labels_np, dtype=torch.long)
        )

class LotteryPredictor:
    def __init__(self, csv_file, window_size, red_balls=5, blue_balls=2):
        self.data = pd.read_csv(csv_file)

        # 映射列名到代码中预期的列名
        self.data.rename(columns={
            '红球_1': 'Red_1',
            '红球_2': 'Red_2',
            '红球_3': 'Red_3',
            '红球_4': 'Red_4',
            '红球_5': 'Red_5',
            '蓝球_1': 'Blue_1',
            '蓝球_2': 'Blue_2'
        }, inplace=True)

        self.scaler_X = MinMaxScaler()
        self.features, self.labels = self.preprocess(self.data, window_size, red_balls, blue_balls)

    def preprocess(self, data, window_size, red_balls, blue_balls):
        features, labels = [], []
        expected_columns = 1 + red_balls + blue_balls
        if len(data.columns) < expected_columns:
            raise ValueError(f"数据列数不足，当前列数: {len(data.columns)}，期望至少 {expected_columns} 列。")

        for i in range(len(data) - window_size):
            # 特征：选取窗口内的红球和蓝球数据
            feature_window = data.iloc[i:i + window_size, 1:1 + red_balls + blue_balls].values
            features.append(feature_window)

            # 标签：下一期的红球和蓝球
            red_labels_seq = data.iloc[i + window_size, 1:1 + red_balls].values - 1  # 减1使其从0开始
            blue_label = data.iloc[i + window_size, 1 + red_balls:1 + red_balls + blue_balls].values - 1
            combined_labels = np.concatenate((red_labels_seq, blue_label))
            labels.append(combined_labels)

        # 转换为 NumPy 数组并进行缩放
        features_np = np.array(features)  # 形状: (num_samples, window_size, feature_dim)
        features_scaled = self.scaler_X.fit_transform(features_np.reshape(-1, features_np.shape[-1])).reshape(features_np.shape)

        labels_np = np.array(labels)  # 形状: (num_samples, total_labels)

        return (
            torch.tensor(features_scaled, dtype=torch.float32),
            torch.tensor(labels_np, dtype=torch.long)
        )
    
# ---------------- 日志信号发射器 ----------------
class LogEmitter(QObject):
    new_log = pyqtSignal(str)

# ---------------- 模型加载函数 ----------------
def load_pytorch_model(model_path, input_dim, hidden_dim, output_dim, output_seq_length, lottery_type="dlt"):
    """加载PyTorch模型"""
    import torch
    from model import LstmCRFModel
    import joblib
    import os

    # 检查模型文件是否存在
    if not os.path.exists(model_path):
        raise FileNotFoundError(f"模型文件不存在：{model_path}")

    # 加载检查点
    checkpoint = torch.load(model_path, map_location=torch.device('cpu'))
    
    # 检查检查点格式并加载模型
    # 新增：支持直接格式（模型状态直接在检查点中）
    if isinstance(checkpoint, dict):
        # 检查常见的键
        if 'model' in checkpoint:
            # 旧格式：包装在'model'键中
            if lottery_type == "plw":
                plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=1)
                plw_model.load_state_dict(checkpoint['model'])
                plw_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return plw_model, None, scaler_X
            if lottery_type == "fc3d":
                fc3d_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=1)
                fc3d_model.load_state_dict(checkpoint['model'])
                fc3d_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return fc3d_model, None, scaler_X    
            elif lottery_type == "kl8":
                # 快乐8旧格式
                kl8_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=1)
                kl8_model.load_state_dict(checkpoint['model'])
                kl8_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return kl8_model, None, scaler_X
            else:
                # 大乐透和双色球旧格式
                red_model = LstmCRFModel(input_dim, hidden_dim, output_dim['red'], output_seq_length['red'], num_layers=1)
                red_model.load_state_dict(checkpoint['red_model'])
                red_model.eval()

                blue_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=1)
                blue_model.load_state_dict(checkpoint['blue_model'])
                blue_model.eval()

                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)

                return red_model, blue_model, scaler_X
        elif 'lstm_model' in checkpoint:
            # 新格式：包装在'lstm_model'键中
            if lottery_type == "plw":
                plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=2)
                plw_model.load_state_dict(checkpoint['lstm_model'])
                plw_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return plw_model, None, scaler_X
            if lottery_type == "fc3d":
                plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=2)
                plw_model.load_state_dict(checkpoint['lstm_model'])
                plw_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return plw_model, None, scaler_X

            elif lottery_type == "kl8":
                # 快乐8新格式
                kl8_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=2)
                kl8_model.load_state_dict(checkpoint['lstm_model'])
                kl8_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return kl8_model, None, scaler_X
            else:
                # 大乐透和双色球的红球蓝球分离模型
                red_lstm_model = LstmCRFModel(input_dim, hidden_dim, output_dim['red'], output_seq_length['red'], num_layers=2)
                red_lstm_model.load_state_dict(checkpoint['red_lstm_model'])
                red_lstm_model.eval()

                blue_lstm_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=2)
                blue_lstm_model.load_state_dict(checkpoint['blue_lstm_model'])
                blue_lstm_model.eval()

                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)

                return red_lstm_model, blue_lstm_model, scaler_X
        elif 'red_lstm_model' in checkpoint:
            # 集成模型格式
            if lottery_type in ["dlt", "ssq"]:
                red_lstm_model = LstmCRFModel(input_dim, hidden_dim, output_dim['red'], output_seq_length['red'], num_layers=2)
                red_lstm_model.load_state_dict(checkpoint['red_lstm_model'])
                red_lstm_model.eval()

                blue_lstm_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=2)
                blue_lstm_model.load_state_dict(checkpoint['blue_lstm_model'])
                blue_lstm_model.eval()

                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)

                return red_lstm_model, blue_lstm_model, scaler_X
            else:
                raise ValueError(f"不支持的模型格式：{lottery_type}")
        elif 'red_model' in checkpoint and 'blue_model' in checkpoint:
            # 大乐透和双色球旧格式（直接包含red_model和blue_model）
            if lottery_type in ["dlt", "ssq"]:
                # 检查模型层数
                num_layers = 2 if 'lstm.weight_ih_l1' in checkpoint['red_model'] else 1
                
                red_model = LstmCRFModel(input_dim, hidden_dim, output_dim['red'], output_seq_length['red'], num_layers=num_layers)
                red_model.load_state_dict(checkpoint['red_model'])
                red_model.eval()

                blue_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=num_layers)
                blue_model.load_state_dict(checkpoint['blue_model'])
                blue_model.eval()

                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)

                return red_model, blue_model, scaler_X
            else:
                raise ValueError(f"不支持的模型格式：{lottery_type}")
        else:
            # 直接格式：检查点本身就是状态字典
            # 检查是否包含LSTM参数来确定层数
            num_layers = 2 if 'lstm.weight_ih_l1' in checkpoint else 1
            
            if lottery_type == "plw":
                plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=num_layers)
                plw_model.load_state_dict(checkpoint)
                plw_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return plw_model, None, scaler_X
            if lottery_type == "fc3d":
                plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=num_layers)
                plw_model.load_state_dict(checkpoint)
                plw_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return plw_model, None, scaler_X
            elif lottery_type == "kl8":
                # 快乐8直接格式
                kl8_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=num_layers)
                kl8_model.load_state_dict(checkpoint)
                kl8_model.eval()
                
                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)
                
                return kl8_model, None, scaler_X
            else:
                # 大乐透和双色球直接格式
                # 检查是否包含红球和蓝球模型的参数
                # 这种情况比较复杂，我们假设检查点是红球模型的状态
                num_layers = 2 if 'lstm.weight_ih_l1' in checkpoint else 1
                
                red_model = LstmCRFModel(input_dim, hidden_dim, output_dim['red'], output_seq_length['red'], num_layers=num_layers)
                red_model.load_state_dict(checkpoint)
                red_model.eval()

                # 对于直接格式，我们需要分别加载红球和蓝球模型
                # 这里我们假设蓝球模型文件是单独的
                blue_model_path = model_path.replace('lstm_model.pth', 'blue_lstm_model.pth')
                if os.path.exists(blue_model_path):
                    blue_checkpoint = torch.load(blue_model_path, map_location=torch.device('cpu'))
                    blue_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=num_layers)
                    blue_model.load_state_dict(blue_checkpoint)
                    blue_model.eval()
                else:
                    # 如果蓝球模型不存在，创建一个默认的
                    blue_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=num_layers)
                    blue_model.eval()

                # 加载缩放器
                scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
                if not os.path.exists(scaler_X_path):
                    raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
                scaler_X = joblib.load(scaler_X_path)

                return red_model, blue_model, scaler_X
    elif isinstance(checkpoint, torch.nn.Module):
        # 检查点本身就是模型实例
        checkpoint.eval()
        # 加载缩放器
        scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
        if not os.path.exists(scaler_X_path):
            raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
        scaler_X = joblib.load(scaler_X_path)
        
        if lottery_type in ["plw", "fc3d", "kl8"]:
            return checkpoint, None, scaler_X
        else:
            return checkpoint, None, scaler_X  # 对于双色球和大乐透，这里可能需要特殊处理
    else:
        # 直接格式：检查点本身就是状态字典
        # 检查是否包含LSTM参数来确定层数
        num_layers = 2 if any('lstm.weight_ih_l1' in key for key in checkpoint.keys()) else 1
        
        if lottery_type == "plw":
            plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=num_layers)
            plw_model.load_state_dict(checkpoint)
            plw_model.eval()
            
            # 加载缩放器
            scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
            if not os.path.exists(scaler_X_path):
                raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
            scaler_X = joblib.load(scaler_X_path)
            
            return plw_model, None, scaler_X
        if lottery_type == "fc3d":
            plw_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=num_layers)
            plw_model.load_state_dict(checkpoint)
            plw_model.eval()
            
            # 加载缩放器
            scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
            if not os.path.exists(scaler_X_path):
                raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
            scaler_X = joblib.load(scaler_X_path)
            
            return plw_model, None, scaler_X
        elif lottery_type == "kl8":
            # 快乐8直接格式
            kl8_model = LstmCRFModel(input_dim, hidden_dim, output_dim, output_seq_length, num_layers=num_layers)
            kl8_model.load_state_dict(checkpoint)
            kl8_model.eval()
            
            # 加载缩放器
            scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
            if not os.path.exists(scaler_X_path):
                raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
            scaler_X = joblib.load(scaler_X_path)
            
            return kl8_model, None, scaler_X
        else:
            # 大乐透和双色球直接格式
            num_layers = 2 if any('lstm.weight_ih_l1' in key for key in checkpoint.keys()) else 1
            
            red_model = LstmCRFModel(input_dim, hidden_dim, output_dim['red'], output_seq_length['red'], num_layers=num_layers)
            red_model.load_state_dict(checkpoint)
            red_model.eval()

            # 对于直接格式，我们需要分别加载红球和蓝球模型
            blue_model_path = model_path.replace('lstm_model.pth', 'blue_lstm_model.pth')
            if os.path.exists(blue_model_path):
                blue_checkpoint = torch.load(blue_model_path, map_location=torch.device('cpu'))
                blue_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=num_layers)
                blue_model.load_state_dict(blue_checkpoint)
                blue_model.eval()
            else:
                # 如果蓝球模型不存在，创建一个默认的
                blue_model = LstmCRFModel(input_dim, hidden_dim, output_dim['blue'], output_seq_length['blue'], num_layers=num_layers)
                blue_model.eval()

            # 加载缩放器
            scaler_X_path = os.path.join(os.path.dirname(model_path), name_path[lottery_type]['scaler_X_file'])
            if not os.path.exists(scaler_X_path):
                raise FileNotFoundError(f"特征缩放器文件不存在：{scaler_X_path}")
            scaler_X = joblib.load(scaler_X_path)

            return red_model, blue_model, scaler_X

def load_resources_pytorch(lottery_type):
    if lottery_type not in name_path:
        raise ValueError(f"不支持的彩票类型：{lottery_type}，请检查输入。")

    # 根据彩票类型设置模型的输入维度和其他参数
    if lottery_type == "dlt":
        input_dim = 7  # 修改为训练时的一致输入维度
        hidden_dim = 128
        output_dim = {
            'red': 35,
            'blue': 12
        }
        output_seq_length = {
            'red': 5,
            'blue': 2
        }
    elif lottery_type == "ssq":
        input_dim = 7  # 修改为训练时的一致输入维度
        hidden_dim = 128
        output_dim = {
            'red': 33,
            'blue': 16
        }
        output_seq_length = {
            'red': 6,
            'blue': 1
        }
    elif lottery_type == "plw":
        input_dim = 5  # 排列5有5个数字位置
        hidden_dim = 128
        output_dim = 10  # 0-9数字
        output_seq_length = 5  # 5个数字位置
    elif lottery_type == "fc3d":
        input_dim = 3  # 3个数字位置
        hidden_dim = 128
        output_dim = 10  # 0-9数字
        output_seq_length = 3  # 3个数字位置
    elif lottery_type == "kl8":
        input_dim = 40  # 快乐8有20个数字，每个数字有号码和权重特征
        hidden_dim = 128
        output_dim = 81  # 快乐8号码范围1-80，加上0作为填充/特殊标记
        output_seq_length = 20  # 快乐8开20个号码

    # 动态生成模型路径
    model_path = os.path.join(name_path[lottery_type]['path'], name_path[lottery_type]['model_file'])

    # 检查模型文件是否存在
    if not os.path.exists(model_path):
        raise FileNotFoundError(f"模型文件不存在：{model_path}")

    # 加载模型和缩放器
    if lottery_type == "plw":
        # 排列5返回单个模型
        plw_model, _, scaler_X = load_pytorch_model(
            model_path, input_dim, hidden_dim, output_dim, output_seq_length, lottery_type
        )
        return plw_model, None, scaler_X
    if lottery_type == "fc3d":
        # 排列5返回单个模型
        plw_model, _, scaler_X = load_pytorch_model(
            model_path, input_dim, hidden_dim, output_dim, output_seq_length, lottery_type
        )
        return plw_model, None, scaler_X
    elif lottery_type == "kl8":
        # 快乐8返回单个模型
        kl8_model, _, scaler_X = load_pytorch_model(
            model_path, input_dim, hidden_dim, output_dim, output_seq_length, lottery_type
        )
        return kl8_model, None, scaler_X
    else:
        # 大乐透和双色球返回红球蓝球模型
        red_model, blue_model, scaler_X = load_pytorch_model(
            model_path, input_dim, hidden_dim, output_dim, output_seq_length, lottery_type
        )
        return red_model, blue_model, scaler_X

# ---------------- 训练线程类 ----------------
class TrainModelThread(QThread):
    log_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, lottery_type):
        super().__init__()
        self.lottery_type = lottery_type

    def run(self):
        try:
            # 根据彩票类型选择训练脚本
            if self.lottery_type == "dlt":
                lstm_train_script = os.path.join(name_path[self.lottery_type]['path'], name_path[self.lottery_type]['lstm_train_script'])
            elif self.lottery_type == "ssq":
                lstm_train_script = os.path.join(name_path[self.lottery_type]['path'], name_path[self.lottery_type]['lstm_train_script'])
            elif self.lottery_type == "plw":
                lstm_train_script = os.path.join(name_path[self.lottery_type]['path'], name_path[self.lottery_type]['lstm_train_script'])
            elif self.lottery_type == "fc3d":
                lstm_train_script = os.path.join(name_path[self.lottery_type]['path'], name_path[self.lottery_type]['lstm_train_script'])
            elif self.lottery_type == "kl8":
                lstm_train_script = os.path.join(name_path[self.lottery_type]['path'], name_path[self.lottery_type]['lstm_train_script'])
            else:
                self.log_signal.emit("未知的彩票类型！")
                return

            if not os.path.exists(lstm_train_script):
                self.log_signal.emit(f"训练脚本不存在：{lstm_train_script}")
                return

            # 使用当前目录下的 Python 解释器
            python_executable = sys.executable  # 自动使用当前运行 GUI 的 Python 解释器

            # 使用 subprocess 调用训练脚本
            process = subprocess.Popen(
                [python_executable, lstm_train_script],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding='utf-8',
                errors='replace',  # 添加错误处理，用替换字符处理无法解码的字节
                bufsize=1,  # 行缓冲
                universal_newlines=True
            )

            for line in process.stdout:
                try:
                    # 确保能够正确处理日志输出
                    clean_line = line.strip()
                    if clean_line:  # 只发送非空行
                        # 进一步清理可能的乱码字符
                        clean_line = clean_line.encode('utf-8', errors='ignore').decode('utf-8')
                        self.log_signal.emit(clean_line)
                except UnicodeDecodeError as decode_error:
                    self.log_signal.emit(f"日志编码警告: {str(decode_error)}")
                except Exception as general_error:
                    self.log_signal.emit(f"日志处理警告: {str(general_error)}")

            process.wait()

            if process.returncode == 0:
                self.log_signal.emit("模型训练完成。")
            else:
                self.log_signal.emit(f"模型训练失败，返回码：{process.returncode}")

        except Exception as e:
            self.log_signal.emit(f"训练过程中发生异常：{str(e)}")

        finally:
            self.finished_signal.emit()


# ---------------- 预测线程类 ----------------
class PredictModelThread(QThread):
    log_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, lottery_type, input_data, model_cache, log_emitter):
        super().__init__()
        self.lottery_type = lottery_type
        self.input_data = input_data
        self.model_cache = model_cache
        self.log_emitter = log_emitter

    def get_deterministic_prediction(self, lottery_type):
        """确定性预测：固定种子的随机预测，确保每次运行结果相同"""
        np.random.seed(42)  # 固定种子确保可重复性
        
        if lottery_type == "dlt":
            red_nums = sorted(np.random.choice(range(1, 36), 5, replace=False))
            blue_nums = sorted(np.random.choice(range(1, 13), 2, replace=False))
        else:
            red_nums = sorted(np.random.choice(range(1, 34), 6, replace=False))
            blue_nums = [np.random.randint(1, 17)]
        
        return red_nums.tolist(), blue_nums

    def load_models_with_cache(self, lottery_type):
        """带缓存机制的模型加载"""
        import os
        import time
        
        # 检查缓存是否存在
        cached_data = self.model_cache.get(lottery_type, {})
        cached_models = cached_data.get('models')
        cached_timestamp = cached_data.get('timestamp')
        
        # 检查模型文件是否存在
        model_path = os.path.join(name_path[lottery_type]['path'], name_path[lottery_type]['model_file'])
        
        if not os.path.exists(model_path):
            raise FileNotFoundError(f"模型文件不存在：{model_path}")
        
        # 获取模型文件的修改时间
        model_mtime = os.path.getmtime(model_path)
        
        # 检查是否需要重新加载（缓存不存在或模型文件已更新）
        if cached_models is None or cached_timestamp is None or model_mtime > cached_timestamp:
            self.log_emitter.new_log.emit(f"🔄 正在加载 {name_path[lottery_type]['name']} 模型...")
            
            # 排列5特殊处理
            if lottery_type == "plw":
                # 排列5只有一个模型，返回格式不同
                plw_model, _, scaler_X = load_resources_pytorch(lottery_type)
                
                # 更新缓存（排列5特殊缓存格式）
                self.model_cache[lottery_type] = {
                    'models': (plw_model, None, scaler_X),  # blue_model为None
                    'timestamp': time.time()
                }
                
                self.log_emitter.new_log.emit(f"✅ {name_path[lottery_type]['name']} 模型加载成功")
                return plw_model, None, 
            if lottery_type == "fc3d":
                # 3d只有一个模型，返回格式不同
                plw_model, _, scaler_X = load_resources_pytorch(lottery_type)
                
                # 更新缓存（排列5特殊缓存格式）
                self.model_cache[lottery_type] = {
                    'models': (plw_model, None, scaler_X),  # blue_model为None
                    'timestamp': time.time()
                }
                
                self.log_emitter.new_log.emit(f"✅ {name_path[lottery_type]['name']} 模型加载成功")
                return plw_model, None, scaler_X
            else:
                # 大乐透和双色球的原有逻辑
                red_model, blue_model, scaler_X = load_resources_pytorch(lottery_type)
                
                # 更新缓存
                self.model_cache[lottery_type] = {
                    'models': (red_model, blue_model, scaler_X),
                    'timestamp': time.time()
                }
                
                self.log_emitter.new_log.emit(f"✅ {name_path[lottery_type]['name']} 模型加载成功")
                return red_model, blue_model, scaler_X
        else:
            # 使用缓存的模型
            self.log_emitter.new_log.emit(f"📋 使用缓存的 {name_path[lottery_type]['name']} 模型")
            return cached_models

    def run(self):
        try:
            # 加载模型
            red_model, blue_model, scaler_X = self.load_models_with_cache(self.lottery_type)

            # 预处理输入数据
            input_data = np.array(self.input_data).reshape(1, -1)
            input_data = scaler_X.transform(input_data)

            # 预测
            if self.lottery_type == "plw":
                # 排列5只有一个模型
                output = red_model(input_data)
                output = output.detach().numpy().flatten()
                predicted_numbers = np.argsort(output)[-5:]
                predicted_numbers = predicted_numbers.tolist()
            elif self.lottery_type == "fc3d":
                # 3d只有一个模型
                output = red_model(input_data)
                output = output.detach().numpy().flatten()
                predicted_numbers = np.argsort(output)[-3:]
                predicted_numbers = predicted_numbers.tolist()
            else:
                # 大乐透和双色球的原有逻辑
                red_output = red_model(input_data)
                blue_output = blue_model(input_data)

                red_output = red_output.detach().numpy().flatten()
                blue_output = blue_output.detach().numpy().flatten()

                red_predicted_numbers = np.argsort(red_output)[-5:]
                blue_predicted_numbers = np.argsort(blue_output)[-2:]

                predicted_numbers = {
                    'red': red_predicted_numbers.tolist(),
                    'blue': blue_predicted_numbers.tolist()
                }

            self.log_signal.emit(f"预测结果：{predicted_numbers}")

        except Exception as e:
            self.log_signal.emit(f"预测过程中发生异常：{str(e)}")

        finally:
            self.finished_signal.emit()


# ---------------- 数据采集线程类 ----------------
class FetchDataThread(QThread):
    """数据采集线程"""
    log_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, lottery_type):
        super().__init__()
        self.lottery_type = lottery_type

    def run(self):
        try:
            # 使用数据采集脚本
            fetch_script = os.path.join(name_path[self.lottery_type]['path'], name_path[self.lottery_type]['fetch_script'])
            
            if not os.path.exists(fetch_script):
                self.log_signal.emit(f"数据采集脚本不存在：{fetch_script}")
                return

            # 使用当前目录下的 Python 解释器
            python_executable = sys.executable

            # 使用 subprocess 调用数据采集脚本
            process = subprocess.Popen(
                [python_executable, fetch_script],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding='utf-8',
                errors='replace',
                bufsize=1,
                universal_newlines=True
            )

            for line in process.stdout:
                try:
                    clean_line = line.strip()
                    if clean_line:
                        clean_line = clean_line.encode('utf-8', errors='ignore').decode('utf-8')
                        self.log_signal.emit(clean_line)
                except Exception as e:
                    self.log_signal.emit(f"日志处理警告: {str(e)}")

            process.wait()

            if process.returncode == 0:
                self.log_signal.emit(f"✅ {name_path[self.lottery_type]['name']}数据采集完成。")
            else:
                self.log_signal.emit(f"❌ {name_path[self.lottery_type]['name']}数据采集失败，返回码：{process.returncode}")

        except Exception as e:
            self.log_signal.emit(f"数据采集过程中发生异常：{str(e)}")

        finally:
            self.finished_signal.emit()


class IncrementalTrainThread(QThread):
    """增量训练线程"""
    log_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, lottery_type):
        super().__init__()
        self.lottery_type = lottery_type

    def run(self):
        try:
            # 使用对应彩票类型的增量训练脚本
            if self.lottery_type == "plw":
                train_script = os.path.join(name_path[self.lottery_type]['path'], 'incremental_train_plw_model.py')
            elif self.lottery_type == "fc3d":
                train_script = os.path.join(name_path[self.lottery_type]['path'], 'incremental_train_3d_model.py')
            elif self.lottery_type == "dlt":
                train_script = os.path.join(name_path[self.lottery_type]['path'], 'incremental_train_dlt_model.py')
            elif self.lottery_type == "ssq":
                train_script = os.path.join(name_path[self.lottery_type]['path'], 'incremental_train_ssq_model.py')
            elif self.lottery_type == "kl8":
                train_script = os.path.join(name_path[self.lottery_type]['path'], 'incremental_train_kl8_model.py')
            else:
                self.log_signal.emit(f"增量训练不支持的彩票类型: {self.lottery_type}")
                return

            if not os.path.exists(train_script):
                self.log_signal.emit(f"增量训练脚本不存在：{train_script}")
                return

            # 使用当前目录下的 Python 解释器
            python_executable = sys.executable

            # 使用 subprocess 调用训练脚本
            process = subprocess.Popen(
                [python_executable, train_script],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding='utf-8',
                errors='replace',
                bufsize=1,
                universal_newlines=True
            )

            for line in process.stdout:
                try:
                    clean_line = line.strip()
                    if clean_line:
                        clean_line = clean_line.encode('utf-8', errors='ignore').decode('utf-8')
                        self.log_signal.emit(clean_line)
                except Exception as e:
                    self.log_signal.emit(f"日志处理警告: {str(e)}")

            process.wait()

            if process.returncode == 0:
                self.log_signal.emit("增量训练完成。")
            else:
                self.log_signal.emit(f"增量训练失败，返回码：{process.returncode}")

        except Exception as e:
            self.log_signal.emit(f"增量训练过程中发生异常：{str(e)}")

        finally:
            self.finished_signal.emit()


class EnhancedLSTMCRFTrainThread(QThread):
    """增强版LSTM-CRF训练线程"""
    log_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, lottery_type):
        super().__init__()
        self.lottery_type = lottery_type

    def run(self):
        try:
            # 使用增强版LSTM-CRF训练脚本
            train_script = os.path.join(name_path[self.lottery_type]['path'], 'train_enhanced_lstm_crf.py')
            
            if not os.path.exists(train_script):
                self.log_signal.emit(f"增强版LSTM-CRF训练脚本不存在：{train_script}")
                return

            # 使用当前目录下的 Python 解释器
            python_executable = sys.executable

            # 使用 subprocess 调用训练脚本
            process = subprocess.Popen(
                [python_executable, train_script],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding='utf-8',
                errors='replace',
                bufsize=1,
                universal_newlines=True
            )

            for line in process.stdout:
                try:
                    clean_line = line.strip()
                    if clean_line:
                        clean_line = clean_line.encode('utf-8', errors='ignore').decode('utf-8')
                        self.log_signal.emit(clean_line)
                except Exception as e:
                    self.log_signal.emit(f"日志处理警告: {str(e)}")

            process.wait()

            if process.returncode == 0:
                self.log_signal.emit("增强版LSTM-CRF模型训练完成。")
            else:
                self.log_signal.emit(f"增强版LSTM-CRF模型训练失败，返回码：{process.returncode}")

        except Exception as e:
            self.log_signal.emit(f"增强版LSTM-CRF训练过程中发生异常：{str(e)}")

        finally:
            self.finished_signal.emit()

class SequenceLSTMTrainThread(QThread):
    """序列LSTM训练线程（支持排列5和fc3d）"""
    log_signal = pyqtSignal(str)
    finished_signal = pyqtSignal()

    def __init__(self, lottery_type):
        super().__init__()
        self.lottery_type = lottery_type

    def run(self):
        try:
            if self.lottery_type not in ["plw", "fc3d"]:
                self.log_signal.emit("⚠️ 序列LSTM训练目前仅支持排列5和fc3d")
                return
            
            # 根据彩票类型确定训练脚本名称
            if self.lottery_type == "plw":
                script_name = "train_plw_sequence_lstm.py"
            elif self.lottery_type == "fc3d":
                script_name = "train_3d_sequence_lstm.py"
            else:
                script_name = f"train_{self.lottery_type}_sequence_lstm.py"
            
            # 使用序列LSTM训练脚本
            train_script = os.path.join(name_path[self.lottery_type]['path'], script_name)
            
            if not os.path.exists(train_script):
                self.log_signal.emit(f"序列LSTM训练脚本不存在：{train_script}")
                return

            # 使用当前目录下的 Python 解释器
            python_executable = sys.executable

            # 使用 subprocess 调用训练脚本
            process = subprocess.Popen(
                [python_executable, train_script],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding='utf-8',
                errors='replace',
                bufsize=1,
                universal_newlines=True
            )

            for line in process.stdout:
                try:
                    clean_line = line.strip()
                    if clean_line:
                        clean_line = clean_line.encode('utf-8', errors='ignore').decode('utf-8')
                        self.log_signal.emit(clean_line)
                except Exception as e:
                    self.log_signal.emit(f"日志处理警告: {str(e)}")

            process.wait()

            if process.returncode == 0:
                self.log_signal.emit("序列LSTM模型训练完成。")
            else:
                self.log_signal.emit(f"序列LSTM模型训练失败，返回码：{process.returncode}")

        except Exception as e:
            self.log_signal.emit(f"序列LSTM训练过程中发生异常：{str(e)}")

        finally:
            self.finished_signal.emit()

# ---------------- 预测结果处理函数 ----------------
def process_predictions(red_predictions, blue_predictions, lottery_type):
    """
    处理预测结果，确保号码在有效范围内且为整数
    :param red_predictions: list, 红球预测的类别索引（排列5时为全部数字）
    :param blue_predictions: list, 蓝球预测的类别索引（排列5时为None）
    :param lottery_type: 'ssq'、'dlt' 或 'plw'
    :return: list, 预测的开奖号码
    """
    if lottery_type == "dlt":
        # 大乐透前区：1-35，后区：1-12
        front_numbers = [min(max(int(num) + 1, 1), 35) for num in red_predictions[:5]]
        back_numbers = [min(max(int(num) + 1, 1), 12) for num in blue_predictions[:2]]

        # 确保前区号码唯一
        front_numbers = list(set(front_numbers))
        while len(front_numbers) < 5:
            additional_num = np.random.randint(1, 36)
            if additional_num not in front_numbers:
                front_numbers.append(additional_num)
        front_numbers = sorted(front_numbers)[:5]
        
        return front_numbers + back_numbers

    elif lottery_type == "ssq":
        # 双色球红球：1-33，蓝球：1-16
        front_numbers = [min(max(int(num) + 1, 1), 33) for num in red_predictions[:6]]
        back_number = min(max(int(blue_predictions[0]) + 1, 1), 16)

        # 确保红球号码唯一
        front_numbers = list(set(front_numbers))
        while len(front_numbers) < 6:
            additional_num = np.random.randint(1, 34)
            if additional_num not in front_numbers:
                front_numbers.append(additional_num)
        front_numbers = sorted(front_numbers)[:6]
        
        return front_numbers + [back_number]
        
    elif lottery_type == "plw":
        # 排列5：5个数字，每个数字范围 0-9
        # red_predictions 实际上包含了5个数字位置的预测
        numbers = [min(max(int(num), 0), 9) for num in red_predictions[:5]]
        return numbers  # 返回处理后的数字列表

import os
import sys
import time
import random
import pickle
import numpy as np
import pandas as pd
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QLabel, QPushButton, QTextEdit, QLineEdit, QFileDialog, QMessageBox
from PyQt5.QtCore import pyqtSignal, QObject

# ---------------- 日志发射器 ----------------
class LogEmitter(QObject):
    new_log = pyqtSignal(str)

# ---------------- 彩票生成器 ----------------
def generate_lottery_numbers(lottery_type):
    if lottery_type == 'ssq':
        # 双色球：6个红球（1-33），1个蓝球（1-16）
        red_balls = random.sample(range(1, 34), 6)
        blue_ball = random.randint(1, 16)
        return sorted(red_balls) + [blue_ball]
        
    elif lottery_type == 'dlt':
        # 大乐透：5个红球（1-35），2个蓝球（1-12）
        red_balls = random.sample(range(1, 36), 5)
        blue_balls = random.sample(range(1, 13), 2)
        return sorted(red_balls) + sorted(blue_balls)
        
    elif lottery_type == 'plw':
        # 排列5不需要去重，允许重复数字
        numbers = [random.randint(0, 9) for _ in range(5)]
        return numbers
        
    else:
        raise ValueError("不支持的彩票类型！请选择 'ssq'、'dlt' 或 'plw'。")

# ---------------- 主窗口类 ----------------
class LotteryPredictorApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.model_cache = {}  # 模型缓存字典
        self.base_dir = os.path.dirname(os.path.abspath(__file__))
        self.algorithm_priorities = {
            "lstm_crf": 1,
            "enhanced_lstm_crf": 2,
            "gradient_boost": 3,
            "memory_network": 4,
            "weighted_expert": 5,
            "markov_chain": 6,
            "enhanced_lstm": 7
        }
        # 初始化日志发射器
        self.log_emitter = LogEmitter()
        
        # 初始化UI
        self.init_ui()
        
        # 程序启动时默认自动加载大乐透模型
        self.auto_load_dlt_model()

    def auto_load_dlt_model(self):
        """程序启动时自动加载大乐透模型"""
        try:
            # 检查大乐透模型文件是否存在
            dlt_model_path = os.path.join(self.base_dir, 'scripts', 'dlt', 'dlt_model.pth')
            if os.path.exists(dlt_model_path):
                self.log_emitter.new_log.emit("🔄 程序启动时自动加载大乐透模型...")
                # 使用缓存机制加载大乐透模型
                self.load_models_with_cache("dlt")
                self.log_emitter.new_log.emit("✅ 大乐透模型加载成功")
            else:
                self.log_emitter.new_log.emit("⚠️ 大乐透模型文件不存在，跳过自动加载")
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 自动加载大乐透模型失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")

    def init_ui(self):
        self.setWindowTitle("彩票预测应用程序 - 多算法智能预测系统")
        self.setGeometry(100, 100, 1000, 700)

        # 主布局
        main_layout = QVBoxLayout()

        # 创建页签卡片容器
        self.tab_widget = QTabWidget()

        # 为每个彩票类型创建页签
        self.lottery_tabs = {}
        for lottery_key, lottery_info in name_path.items():
            tab_widget = self.create_lottery_tab(lottery_key, lottery_info)
            self.lottery_tabs[lottery_key] = tab_widget
            self.tab_widget.addTab(tab_widget, lottery_info['name'])
        
        main_layout.addWidget(self.tab_widget)

        # GPU状态显示区域
        gpu_status_group = QGroupBox("🖥️ GPU/CPU状态")
        gpu_status_layout = QHBoxLayout()
        
        # 检查GPU状态
        gpu_status_text = self.get_gpu_status_text()
        gpu_status_label = QLabel(gpu_status_text)
        gpu_status_label.setStyleSheet("color: #333; font-weight: bold; padding: 5px;")
        gpu_status_layout.addWidget(gpu_status_label)
        
        gpu_status_group.setLayout(gpu_status_layout)
        main_layout.addWidget(gpu_status_group)

        # 全局日志显示区域
        log_group = QGroupBox("📜 系统日志")
        log_layout = QVBoxLayout()
        
        self.log_box = QTextEdit()
        self.log_box.setReadOnly(True)
        self.log_box.setMaximumHeight(400)
        log_layout.addWidget(self.log_box)
        
        log_group.setLayout(log_layout)
        main_layout.addWidget(log_group)

        # 设置中心窗口
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

        # 连接日志发射器
        self.log_emitter.new_log.connect(self.log_box.append)

        # 连接页签切换信号
        self.tab_widget.currentChanged.connect(self.on_tab_changed)
        
        # 初始化当前选中的彩票类型
        self.current_lottery_type = list(name_path.keys())[0]
    
    def get_gpu_status_text(self):
        """获取GPU状态文本"""
        try:
            if TORCH_AVAILABLE:
                import torch
                if torch.cuda.is_available():
                    gpu_name = torch.cuda.get_device_name()
                    gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
                    return f"🟢 GPU可用: {gpu_name} ({gpu_memory:.1f}GB显存)"
                else:
                    return "🟡 CUDA不可用，使用CPU训练模式"
            else:
                return "🔴 PyTorch不可用"
        except Exception as e:
            return f"🔴 GPU状态检查失败: {str(e)}"
    
    def create_lottery_tab(self, lottery_key, lottery_info):
        """为每个彩票类型创建专用页签"""
        tab_widget = QWidget()
        tab_layout = QVBoxLayout()
        
        # 彩票信息卡片
        info_group = QGroupBox(f"🎯 {lottery_info['name']} 信息")
        info_layout = QVBoxLayout()
        
        # 彩票规则显示
        if lottery_key == "dlt":
            rules_text = "🔴 前区：5个号码 (01-35) | 🔵 后区：2个号码 (01-12)"
        elif lottery_key == "ssq":
            rules_text = "🔴 红球：6个号码 (01-33) | 🔵 蓝球：1个号码 (01-16)"
        elif lottery_key == "fc3d":
            rules_text = "🔴 数字：3个位置 (百十个位，每位 0-9，允许重复)"
        else:  # plw
            rules_text = "📟 数字：5个位置 (万千百十个位，每位 0-9，允许重复)"
        
        # 创建水平布局，将规则文字和采集按钮放在同一行
        rules_layout = QHBoxLayout()
        
        rules_label = QLabel(rules_text)
        rules_label.setStyleSheet("color: #666; font-size: 12px; padding: 5px;")
        rules_layout.addWidget(rules_label)
                
        # 添加数据采集按钮
        fetch_data_btn = QPushButton("🔄 采集最新数据")
        fetch_data_btn.setObjectName(f"fetch_data_btn_{lottery_key}")
        # 修复Lambda表达式问题，使用偏函数或直接方法引用
        fetch_data_btn.clicked.connect(lambda checked, lk=lottery_key: self.fetch_latest_data(lk))
        rules_layout.addWidget(fetch_data_btn)
        
        # 添加弹性空间，将按钮推到右侧
        rules_layout.addStretch()
        
        info_layout.addLayout(rules_layout)
                
        info_group.setLayout(info_layout)
        tab_layout.addWidget(info_group)
        
        # 算法列表卡片
        algo_group = QGroupBox(f"🤖 {lottery_info['name']} 可用算法")
        algo_layout = QVBoxLayout()
        
        # 定义每个彩票类型的算法列表
        algorithms = self.get_available_algorithms(lottery_key)
        
        # 算法选择组件
        algo_select_layout = QHBoxLayout()
        algo_select_layout.addWidget(QLabel("选择算法:"))
        
        algorithm_combo = QComboBox()
        algorithm_combo.addItems([algo['name'] for algo in algorithms])
        algorithm_combo.setObjectName(f"algorithm_combo_{lottery_key}")
        algo_select_layout.addWidget(algorithm_combo)
        
        algo_layout.addLayout(algo_select_layout)
        
        # 预测数量选择
        pred_count_layout = QHBoxLayout()
        pred_count_layout.addWidget(QLabel("预测数量:"))
        
        prediction_spin = QSpinBox()
        prediction_spin.setRange(1, 20)
        prediction_spin.setValue(1)
        prediction_spin.setObjectName(f"prediction_spin_{lottery_key}")
        pred_count_layout.addWidget(prediction_spin)
        
        algo_layout.addLayout(pred_count_layout)
        
        # 预测按钮组
        predict_buttons_layout = QHBoxLayout()
        
        # 单一算法预测按钮
        predict_btn = QPushButton("🎯 生成预测")
        predict_btn.setObjectName(f"predict_btn_{lottery_key}")
        predict_btn.clicked.connect(lambda: self.generate_single_prediction(lottery_key))
        predict_buttons_layout.addWidget(predict_btn)
        
        # 多算法预测按钮
        multi_predict_btn = QPushButton("🎆 多算法预测")
        multi_predict_btn.setObjectName(f"multi_predict_btn_{lottery_key}")
        multi_predict_btn.clicked.connect(lambda: self.generate_multi_prediction(lottery_key))
        predict_buttons_layout.addWidget(multi_predict_btn)
        
        # 统一对比预测按钮
        compare_predict_btn = QPushButton("🔥 统一对比预测")
        compare_predict_btn.setObjectName(f"compare_predict_btn_{lottery_key}")
        compare_predict_btn.clicked.connect(lambda: self.generate_compare_prediction(lottery_key))
        predict_buttons_layout.addWidget(compare_predict_btn)
        
        algo_layout.addLayout(predict_buttons_layout)
        algo_group.setLayout(algo_layout)
        tab_layout.addWidget(algo_group)
        
        # 模型训练卡片
        train_group = QGroupBox(f"🎓 {lottery_info['name']} 模型训练")
        train_layout = QVBoxLayout()
        
        # 模型状态显示
        model_status_label = QLabel(f"模型状态：未检查")
        model_status_label.setObjectName(f"model_status_{lottery_key}")
        train_layout.addWidget(model_status_label)
        
        # 训练按钮组
        train_buttons_layout = QHBoxLayout()
        
        # LSTM-CRF训练按钮
        lstm_train_btn = QPushButton(f"🤖 训练 LSTM-CRF 模型")
        lstm_train_btn.setObjectName(f"lstm_train_btn_{lottery_key}")
        lstm_train_btn.clicked.connect(lambda: self.train_lstm_model(lottery_key))
        train_buttons_layout.addWidget(lstm_train_btn)
        
        # 序列LSTM训练按钮（仅排列5）
        if lottery_key == "plw":
            seq_lstm_train_btn = QPushButton("🔥 训练 序列LSTM 模型")
            seq_lstm_train_btn.setObjectName(f"seq_lstm_train_btn_{lottery_key}")
            seq_lstm_train_btn.clicked.connect(lambda: self.train_sequence_lstm_model(lottery_key))
            train_buttons_layout.addWidget(seq_lstm_train_btn)
        
        if lottery_key == "fc3d":
            seq_lstm_train_btn = QPushButton("🔥 3d训练 序列LSTM 模型")
            seq_lstm_train_btn.setObjectName(f"seq_lstm_train_btn_{lottery_key}")
            seq_lstm_train_btn.clicked.connect(lambda: self.train_sequence_lstm_model(lottery_key))
            train_buttons_layout.addWidget(seq_lstm_train_btn)

        # 增强版LSTM-CRF训练按钮（所有彩票类型）
        enhanced_crf_train_btn = QPushButton("💎 训练 增强版LSTM-CRF 模型")
        enhanced_crf_train_btn.setObjectName(f"enhanced_crf_train_btn_{lottery_key}")
        enhanced_crf_train_btn.clicked.connect(lambda: self.train_enhanced_lstm_crf_model(lottery_key))
        train_buttons_layout.addWidget(enhanced_crf_train_btn)
        
        # 增量训练按钮（所有彩票类型）
        incremental_train_btn = QPushButton("🔄 增量训练 模型")
        incremental_train_btn.setObjectName(f"incremental_train_btn_{lottery_key}")
        incremental_train_btn.clicked.connect(lambda: self.train_incremental_model(lottery_key))
        train_buttons_layout.addWidget(incremental_train_btn)
        
        # 模型重加载按钮
        reload_btn = QPushButton("🔄 重加载模型")
        reload_btn.setObjectName(f"reload_btn_{lottery_key}")
        reload_btn.clicked.connect(lambda: self.reload_specific_model(lottery_key))
        train_buttons_layout.addWidget(reload_btn)
        
        train_layout.addLayout(train_buttons_layout)
        train_group.setLayout(train_layout)
        tab_layout.addWidget(train_group)
        
        # 预测结果显示区域
        result_group = QGroupBox(f"🏆 {lottery_info['name']} 预测结果")
        result_layout = QVBoxLayout()
        
        result_display = QTextEdit()
        result_display.setReadOnly(True)
        result_display.setMaximumHeight(600)  # 原来是300，现在加高一倍到600
        result_display.setObjectName(f"result_display_{lottery_key}")
        result_display.setPlainText("预测结果将在此显示...")
        result_layout.addWidget(result_display)
        
        result_group.setLayout(result_layout)
        tab_layout.addWidget(result_group)
        
        tab_widget.setLayout(tab_layout)
        return tab_widget
    
    def get_available_algorithms(self, lottery_key):
        """获取每个彩票类型可用的算法列表"""
        base_algorithms = [
            {"name": "🤖 LSTM-CRF 经典模式", "method": "lstm_crf", "has_model": True},
            {"name": "🚀 梯度提升 特征模式", "method": "gradient_boost", "has_model": False},
            {"name": " 模式记忆 网络模式", "method": "memory_network", "has_model": False},
            {"name": "⚖️ 权重专家 系统模式", "method": "weighted_expert", "has_model": False}
        ]
        
        # 添加增强版LSTM-CRF
        base_algorithms.append({
            "name": "💎 LSTM-CRF 经典模式增强版", 
            "method": "enhanced_lstm_crf", 
            "has_model": True
        })
        
        # 排列5特有的算法
        if lottery_key == "plw":
            # 序列LSTM增强模式
            base_algorithms.append({
                "name": "🔥 序列LSTM 增强模式", 
                "method": "enhanced_lstm", 
                "has_model": True,
                "special": "排列5专用"
            })
            
            # 添加马尔可夫链模型
            base_algorithms.append({
                "name": "🔗 马尔可夫链 模型", 
                "method": "markov_chain", 
                "has_model": False
            })
        if lottery_key == "fc3d":
            # 3d序列LSTM增强模式
            base_algorithms.append({
                "name": "🔥 3d序列LSTM 增强模式", 
                "method": "enhanced_lstm", 
                "has_model": True,
                "special": "3d专用"
            })
            
            # 添加马尔可夫链模型
            base_algorithms.append({
                "name": "🔗 3d马尔可夫链 模型", 
                "method": "markov_chain", 
                "has_model": False
            })
        # 快乐8特有的算法
        elif lottery_key == "kl8":
            # 序列LSTM增强模式
            base_algorithms.append({
                "name": "🔥 序列LSTM 增强模式", 
                "method": "enhanced_lstm", 
                "has_model": True,
                "special": "快乐8专用"
            })
        else:
            # 大乐透和双色球的序列LSTM（未实现真实模型）
            base_algorithms.append({
                "name": "🔥 序列LSTM 增强模式", 
                "method": "enhanced_lstm", 
                "has_model": False,
                "note": "确定性算法"
            })
        
        return base_algorithms
    
    def on_tab_changed(self, index):
        """页签切换事件处理"""
        lottery_keys = list(name_path.keys())
        if 0 <= index < len(lottery_keys):
            self.current_lottery_type = lottery_keys[index]
            lottery_name = name_path[self.current_lottery_type]['name']
            self.log_emitter.new_log.emit(f"🔄 切换到 {lottery_name} 页签")
            
            # 更新模型状态显示
            self.update_model_status(self.current_lottery_type)
    
    def update_model_status(self, lottery_key):
        """更新模型状态显示"""
        try:
            model_status_label = self.findChild(QLabel, f"model_status_{lottery_key}")
            if model_status_label:
                # 检查模型文件状态
                import os
                import time
                
                model_path = os.path.join(name_path[lottery_key]['path'], name_path[lottery_key]['model_file'])
                
                if os.path.exists(model_path):
                    mtime = os.path.getmtime(model_path)
                    mtime_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(mtime))
                    file_size = os.path.getsize(model_path) / (1024 * 1024)  # MB
                    status_text = f"✅ LSTM-CRF模型已存在 | 最后更新: {mtime_str} | 大小: {file_size:.2f}MB"
                else:
                    status_text = "⚠️ LSTM-CRF模型未训练 | 点击训练按钮开始训练"
                
                # 检查序列LSTM模型（排列5和3D）
                if lottery_key == "plw":
                    seq_model_path = os.path.join(name_path[lottery_key]['path'], 'plw_sequence_lstm_model.pth')
                    if os.path.exists(seq_model_path):
                        seq_mtime = os.path.getmtime(seq_model_path)
                        seq_mtime_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(seq_mtime))
                        seq_file_size = os.path.getsize(seq_model_path) / (1024 * 1024)
                        status_text += f"\n✅ 序列LSTM模型已存在 | 最后更新: {seq_mtime_str} | 大小: {seq_file_size:.2f}MB"
                    else:
                        status_text += "\n⚠️ 序列LSTM模型未训练 | 点击训练按钮开始训练"
                elif lottery_key == "fc3d":
                    seq_model_path = os.path.join(name_path[lottery_key]['path'], name_path[lottery_key].get('sequence_lstm_model_file', 'fc3d_sequence_lstm_model.pth'))
                    if os.path.exists(seq_model_path):
                        seq_mtime = os.path.getmtime(seq_model_path)
                        seq_mtime_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(seq_mtime))
                        seq_file_size = os.path.getsize(seq_model_path) / (1024 * 1024)
                        status_text += f"\n✅ 序列LSTM模型已存在 | 最后更新: {seq_mtime_str} | 大小: {seq_file_size:.2f}MB"
                    else:
                        status_text += "\n⚠️ 序列LSTM模型未训练 | 点击训练按钮开始训练"
                
                # 检查增强版LSTM-CRF模型（所有彩票类型）
                enhanced_crf_model_path = os.path.join(name_path[lottery_key]['path'], 'enhanced_lstm_crf_model.pth')
                if os.path.exists(enhanced_crf_model_path):
                    enhanced_crf_mtime = os.path.getmtime(enhanced_crf_model_path)
                    enhanced_crf_mtime_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(enhanced_crf_mtime))
                    enhanced_crf_file_size = os.path.getsize(enhanced_crf_model_path) / (1024 * 1024)
                    status_text += f"\n✅ 增强版LSTM-CRF模型已存在 | 最后更新: {enhanced_crf_mtime_str} | 大小: {enhanced_crf_file_size:.2f}MB"
                else:
                    status_text += "\n⚠️ 增强版LSTM-CRF模型未训练 | 点击训练按钮开始训练"
                
                model_status_label.setText(status_text)
                model_status_label.setStyleSheet("color: #333; font-size: 11px; padding: 8px; background-color: #f5f5f5; border-radius: 4px;")
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 更新模型状态失败: {e}")
    
    def generate_single_prediction(self, lottery_key):
        """生成单一算法预测"""
        try:
            # 获取当前页签的组件
            algorithm_combo = self.findChild(QComboBox, f"algorithm_combo_{lottery_key}")
            prediction_spin = self.findChild(QSpinBox, f"prediction_spin_{lottery_key}")
            result_display = self.findChild(QTextEdit, f"result_display_{lottery_key}")
            
            if not all([algorithm_combo, prediction_spin, result_display]):
                self.log_emitter.new_log.emit("⚠️ 无法找到UI组件")
                return
            
            # 获取选中的算法和预测数量
            algorithm_index = algorithm_combo.currentIndex()
            num_predictions = prediction_spin.value()
            
            algorithms = self.get_available_algorithms(lottery_key)
            selected_algorithm = algorithms[algorithm_index]
            
            self.log_emitter.new_log.emit(f"🎯 开始 {name_path[lottery_key]['name']} {selected_algorithm['name']} 预测...")
            
            # 调用相应的预测方法
            predictions = self.call_algorithm_method(selected_algorithm['method'], lottery_key, num_predictions)
            
            # 显示结果
            self.display_predictions_in_tab(lottery_key, predictions, selected_algorithm['name'])
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 单一算法预测失败: {str(e)}")
    
    def generate_multi_prediction(self, lottery_key):
        """生成多算法预测"""
        try:
            prediction_spin = self.findChild(QSpinBox, f"prediction_spin_{lottery_key}")
            num_predictions = prediction_spin.value() if prediction_spin else 1
            
            self.log_emitter.new_log.emit(f"🎆 开始 {name_path[lottery_key]['name']} 多算法预测...")
            
            # 调用现有的多算法预测方法
            self.generate_multi_algorithm_prediction()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 多算法预测失败: {str(e)}")
    
    def generate_compare_prediction(self, lottery_key):
        """生成统一对比预测"""
        try:
            prediction_spin = self.findChild(QSpinBox, f"prediction_spin_{lottery_key}")
            num_predictions = prediction_spin.value() if prediction_spin else 1
            
            self.log_emitter.new_log.emit(f"🔥 开始 {name_path[lottery_key]['name']} 统一对比预测...")
            
            # 调用现有的统一对比预测方法
            self.generate_comprehensive_multi_algorithm_prediction_internal(lottery_key, num_predictions)
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 统一对比预测失败: {str(e)}")
    
    def generate_comprehensive_multi_algorithm_prediction_internal(self, lottery_key, num_predictions, result_display=None):
        """内部统一多算法对比预测处理"""
        try:
            from datetime import datetime
            
            # 定义所有可用的算法
            algorithms = [
                {"name": "💎 LSTM-CRF 经典模式增强版", "method": "enhanced_lstm_crf"},
                {"name": "🤖 LSTM-CRF 经典模式", "method": "lstm_crf"},
                {"name": "🔥 序列LSTM 增强模式", "method": "enhanced_lstm"},
                {"name": "📈 梯度提升 特征模式", "method": "gradient_boost"},
                {"name": " 模式记忆 网络模式", "method": "memory_network"},
                {"name": "⚖️ 权重专家 系统模式", "method": "weighted_expert"}
              
            ]
            
            lottery_name = name_path[lottery_key]['name']
            
            result_text = f"🔥 {lottery_name} 统一多算法对比预测\n"
            result_text += f"预测时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
            result_text += "=" * 80 + "\n\n"
            
            all_predictions = {}
            successful_count = 0
            
            # 执行所有算法
            for algo in algorithms:
                try:
                    self.log_emitter.new_log.emit(f"🔄 正在执行 {algo['name']}...")
                    
                    predictions = self.call_algorithm_method(algo['method'], lottery_key, num_predictions)
                    all_predictions[algo['name']] = {
                        'success': True,
                        'predictions': predictions
                    }
                    successful_count += 1
                    
                    result_text += f"{algo['name']}:\n"
                    for j, pred in enumerate(predictions, 1):
                        # 确保pred是字典且包含必要的键
                        if isinstance(pred, dict):
                            # 获取预测数字，确保不是None
                            red_balls = pred.get('red', [])
                            blue_balls = pred.get('blue', [])
                            
                            # 对于排列5，如果red_balls是None，则使用空列表
                            if red_balls is None:
                                red_balls = []
                            
                            # 转换np.int64类型为普通int
                            red_balls = [int(num) if isinstance(num, (np.integer, np.floating)) else num for num in red_balls]
                            blue_balls = [int(num) if isinstance(num, (np.integer, np.floating)) else num for num in blue_balls]
                            
                            # 根据彩票类型调整显示格式
                            if lottery_key == "dlt":
                                result_text += f"  第{j}组: 前区{red_balls} | 后区{blue_balls}\n"
                            elif lottery_key == "plw":
                                result_text += f"  第{j}组: 数字 {red_balls}\n"
                            elif lottery_key == "kl8":
                                # 快乐8有10个数字，没有蓝球
                                result_text += f"  第{j}组: 号码 {red_balls}\n"
                                # 如果有权重信息，显示前10个权重最高的号码
                                if 'weights' in pred and pred['weights']:
                                    weights = pred['weights']
                                    # 转换权重为普通float
                                    weights = {int(k): float(v) if isinstance(v, (np.integer, np.floating)) else v 
                                              for k, v in weights.items()}
                                    # 按权重排序，获取前10个
                                    sorted_weights = sorted(weights.items(), key=lambda x: x[1], reverse=True)
                                    result_text += f"    前10个权重最高的号码: "
                                    for k, (num, weight) in enumerate(sorted_weights[:10]):
                                        # 将权重转换为百分比形式显示
                                        percentage = (weight / sum(weights.values())) * 100 if sum(weights.values()) > 0 else 0
                                        result_text += f"{num}({percentage:.2f}%) "
                                    result_text += "\n"
                                # 如果有置信度，显示置信度
                                if 'confidence' in pred:
                                    result_text += f"    置信度: {pred['confidence']:.3f}\n"

                            elif lottery_key == "ssq":
                                result_text += f"  第{j}组: 红球{red_balls} | 蓝球{blue_balls}\n"
                            else:  # 其他彩票类型
                                # 默认处理，避免显示"蓝球[]"
                                if lottery_key == "kl8":
                                    result_text += f"  第{j}组: 号码 {red_balls}\n"
                                else:
                                    result_text += f"  第{j}组: 红球{red_balls} | 蓝球{blue_balls}\n"
                            if 'confidence' in pred:
                                result_text += f"    置信度: {pred['confidence']:.3f}\n"
                        else:
                            # 如果pred不是字典，显示默认信息
                            if lottery_key == "dlt":
                                result_text += f"  第{j}组: 前区[] | 后区[]\n"
                            elif lottery_key == "plw":
                                result_text += f"  第{j}组: 数字 []\n"
                            elif lottery_key == "kl8":
                                # 快乐8有20个数字，没有蓝球
                                result_text += f"  第{j}组: 号码 []\n"
                            else:  # ssq和其他类型
                                result_text += f"  第{j}组: 红球[] | 蓝球[]\n"
                    
                    result_text += "\n"
                    
                except Exception as algo_error:
                    all_predictions[algo['name']] = {
                        'success': False,
                        'error': str(algo_error)
                    }
                    # 特殊处理：当LSTM-CRF经典模式在大乐透和双色球模式下执行失败时，提示特定信息
                    if algo['method'] == "lstm_crf" and lottery_key in ["dlt", "ssq"]:
                        error_message = "LSTM-CRF 经典模式，无法生成预测"
                        result_text += f"{algo['name']}: ❗ 执行失败 - {error_message}\n\n"
                        self.log_emitter.new_log.emit(f"⚠️ {algo['name']} 执行失败: {error_message}")
                    else:
                        result_text += f"{algo['name']}: ❗ 执行失败 - {str(algo_error)}\n\n"
                        self.log_emitter.new_log.emit(f"⚠️ {algo['name']} 执行失败: {algo_error}")
            
            # 一致性分析
            consistency_analysis = self.analyze_prediction_consistency(all_predictions, lottery_key)
            result_text += consistency_analysis
            
            # 统计信息
            result_text += "\n" + "=" * 80 + "\n"
            result_text += f"📊 统计信息:\n"
            result_text += f"  成功算法: {successful_count}/6\n"
            result_text += f"  生成时间: {datetime.now().strftime('%H:%M:%S')}\n"
            
            # 显示结果
            if result_display:
                result_display.setPlainText(result_text)
            else:
                # 如果没有指定显示组件，查找当前页签的结果显示区域
                current_result_display = self.findChild(QTextEdit, f"result_display_{lottery_key}")
                if current_result_display:
                    current_result_display.setPlainText(result_text)
                else:
                    # 如果还找不到，使用原有的result_label
                    if hasattr(self, 'result_label'):
                        self.update_current_tab_result(result_text)
            
            self.log_emitter.new_log.emit(f"✅ {lottery_name} 统一多算法对比预测完成，成功算法: {successful_count}/6")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 内部多算法预测失败: {str(e)}")
    
    def call_algorithm_method(self, method, lottery_key, num_predictions):
        """调用指定的算法方法"""
        if method == "lstm_crf":
            return self.get_lstm_crf_predictions(lottery_key, num_predictions)
        elif method == "enhanced_lstm":
            return self.get_enhanced_lstm_predictions(lottery_key, num_predictions)
        elif method == "gradient_boost":
            return self.get_gradient_boost_predictions(lottery_key, num_predictions)
        elif method == "memory_network":
            return self.get_memory_network_predictions(lottery_key, num_predictions)
        elif method == "markov_chain":
            return self.get_markov_chain_predictions(lottery_key, num_predictions)
        elif method == "memory_markov_combined":
            return self.get_memory_markov_combined_predictions(lottery_key, num_predictions)
        elif method == "weighted_expert":
            return self.get_weighted_expert_predictions(lottery_key, num_predictions)
        elif method == "enhanced_lstm_crf":
            return self.get_enhanced_lstm_crf_predictions(lottery_key, num_predictions)
        else:
            raise ValueError(f"未知的算法方法: {method}")
    
    def display_predictions_in_tab(self, lottery_key, predictions, algorithm_name):
        """在指定页签中显示预测结果"""
        try:
            result_display = self.findChild(QTextEdit, f"result_display_{lottery_key}")
            if not result_display:
                return
            
            # 清空之前的结果
            result_display.clear()
            
            # 格式化显示结果
            from datetime import datetime
            result_text = f"🎯 {name_path[lottery_key]['name']} - {algorithm_name}\n"
            result_text += f"预测时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
            result_text += "=" * 50 + "\n\n"
            
            # 显示每组预测结果
            for i, pred in enumerate(predictions, 1):
                result_text += f"第{i}组预测：\n"
                
                # 根据彩票类型调整显示格式
                if lottery_key == "dlt":
                    result_text += f"  前区：{pred['red']}\n"
                    result_text += f"  后区：{pred['blue']}\n"
                elif lottery_key == "plw":
                    result_text += f"  数字：{pred['red']}\n"
                elif lottery_key == "kl8":
                    # 快乐8特殊处理：显示10个号码和权重信息
                    result_text += f"  号码：{pred['red']}\n"
                    # 如果有权重信息，显示前10个权重最高的号码
                    if 'weights' in pred and pred['weights']:
                        weights = pred['weights']
                        # 按权重排序，获取前10个
                        sorted_weights = sorted(weights.items(), key=lambda x: x[1], reverse=True)
                        result_text += f"  前10个权重最高的号码：\n"
                        for j, (num, weight) in enumerate(sorted_weights[:10]):
                            result_text += f"    {j+1}. 号码 {num}: 权重 {weight:.3f}\n"
                else:  # ssq
                    result_text += f"  红球：{pred['red']}\n"
                    result_text += f"  蓝球：{pred['blue']}\n"
                
                # 显示置信度（如果有）
                if 'confidence' in pred:
                    result_text += f"  置信度：{pred['confidence']:.3f}\n"
                
                result_text += "\n"
            
            result_display.setPlainText(result_text)
            
            self.log_emitter.new_log.emit(f"✅ {algorithm_name} 预测完成，生成 {len(predictions)} 组结果")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 显示结果失败: {e}")
    
    def update_log(self, message):
        """更新日志显示"""
        self.log_box.append(message)
        self.log_box.ensureCursorVisible()

    def fetch_latest_data(self, lottery_key):
        """采集最新数据"""
        try:
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"🔄 开始采集 {lottery_name} 最新数据...")
            
            # 禁用数据采集按钮防止重复采集
            fetch_data_btn = self.findChild(QPushButton, f"fetch_data_btn_{lottery_key}")
            if fetch_data_btn:
                fetch_data_btn.setEnabled(False)
                fetch_data_btn.setText("🔄 数据采集中...")
            
            # 启动数据采集线程
            self.fetch_thread = FetchDataThread(lottery_key)
            self.fetch_thread.log_signal.connect(self.update_log)
            self.fetch_thread.finished_signal.connect(lambda: self.on_fetch_data_finished(lottery_key))
            self.fetch_thread.start()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 数据采集启动失败: {str(e)}")
    
    def on_fetch_data_finished(self, lottery_key):
        """当数据采集完成时的处理"""
        try:
            # 启用数据采集按钮
            fetch_data_btn = self.findChild(QPushButton, f"fetch_data_btn_{lottery_key}")
            if fetch_data_btn:
                fetch_data_btn.setEnabled(True)
                fetch_data_btn.setText("🔄 采集最新数据")
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"✅ {lottery_name} 数据采集完成")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 数据采集完成处理失败: {e}")

    def update_current_tab_result(self, result_text):
        """更新当前页签的结果显示"""
        current_tab = self.tab_widget.currentWidget()
        if current_tab:
            # 查找当前页签中的结果显示区域
            result_displays = current_tab.findChildren(QTextEdit)
            for display in result_displays:
                # 找到结果显示区域（通常是只读的QTextEdit）
                if display.isReadOnly():
                    display.setPlainText(result_text)
                    break
        
        # 如果还有旧的result_label，也更新一下（保持向后兼容）
        if hasattr(self, 'result_label'):
            self.update_current_tab_result(result_text)
    
    def generate_multi_algorithm_prediction(self):
        """生成多算法预测"""
        # 从当前活跃页签获取彩票类型
        current_tab_index = self.tab_widget.currentIndex()
        lottery_keys = list(name_path.keys())
        
        if current_tab_index < len(lottery_keys):
            selected_key = lottery_keys[current_tab_index]
            lottery_type = selected_key
            lottery_name = name_path[selected_key]['name']
        else:
            # 默认使用第一个彩票类型
            selected_key = lottery_keys[0]
            lottery_type = selected_key
            lottery_name = name_path[selected_key]['name']
        
        # 默认预测3组
        num_predictions = 3
        
        self.log_emitter.new_log.emit(f"🚀 启动多算法预测系统...")
        
        try:
            # 尝试使用多算法系统
            predictions = self.run_multi_algorithm_prediction(lottery_type, num_predictions)
            
            if predictions:
                result_text = f"🎆 {lottery_name} 多算法预测结果：\n"
                result_text += "=" * 50 + "\n"
                
                for i, pred in enumerate(predictions):
                    algorithm_name = pred.get('algorithm', f'算法{i+1}')
                    confidence = pred.get('confidence', 0.5)
                    red_balls = pred.get('red', [])
                    blue_balls = pred.get('blue', [])
                    weights = pred.get('weights', {})
                    
                    # 转换numpy类型为普通Python类型
                    red_balls = [int(num) if isinstance(num, (np.integer, np.floating)) else num for num in red_balls]
                    blue_balls = [int(num) if isinstance(num, (np.integer, np.floating)) else num for num in blue_balls]
                    confidence = float(confidence) if isinstance(confidence, (np.integer, np.floating)) else confidence
                    
                    result_text += f"🤖 {algorithm_name}\n"
                    
                    # 根据彩票类型显示不同的格式
                    if lottery_type == "dlt":
                        result_text += f"   前区：{red_balls}\n"
                        result_text += f"   后区：{blue_balls}\n"
                    elif lottery_type == "plw":
                        result_text += f"   数字：{red_balls}\n"
                    elif lottery_type == "kl8":
                        result_text += f"   号码：{red_balls}\n"
                        # 如果有权重信息，显示前10个权重最高的号码
                        if weights:
                            # 按权重排序，获取前10个
                            sorted_weights = sorted(weights.items(), key=lambda x: x[1], reverse=True)
                            result_text += f"   前10个权重最高的号码: "
                            # 计算总权重用于百分比计算
                            total_weight = sum(weight for _, weight in sorted_weights[:10])
                            for k, (num, weight) in enumerate(sorted_weights[:10]):
                                # 转换权重为普通float
                                weight = float(weight) if isinstance(weight, (np.integer, np.floating)) else weight
                                # 转换为百分比形式显示
                                percentage = (weight / total_weight) * 100 if total_weight > 0 else 0
                                result_text += f"{int(num)}({percentage:.2f}%) "
                            result_text += "\n"
                    else:  # ssq
                        result_text += f"   红球：{red_balls}\n"
                        result_text += f"   蓝球：{blue_balls}\n"
                    
                    result_text += f"   置信度：{confidence:.2f}\n\n"
                
                # 添加统计信息
                total_algorithms = len(predictions)
                avg_confidence = sum(p.get('confidence', 0) for p in predictions) / total_algorithms if total_algorithms > 0 else 0
                
                result_text += f"📊 预测统计：\n"
                result_text += f"   算法数量：{total_algorithms} 个\n"
                result_text += f"   平均置信度：{avg_confidence:.2f}\n"
                result_text += f"   生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
                
                self.update_current_tab_result(result_text)
                self.log_emitter.new_log.emit(f"✅ 多算法预测完成，生成 {len(predictions)} 组结果")
            else:
                self.update_current_tab_result("⚠️ 多算法预测失败，请检查系统设置")
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"❌ 多算法预测出错：{str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            self.update_current_tab_result("多算法预测失败，请查看日志。")
    
    def run_multi_algorithm_prediction(self, lottery_type, num_predictions):
        """运行多算法预测（使用内置算法）"""
        try:
            self.log_emitter.new_log.emit("💻 使用内置多算法系统")
            
            # 直接使用内置的算法方法
            algorithms = [
                "💎 LSTM-CRF 经典模式增强版",
                "🤖 LSTM-CRF 经典模式",
                "🔥 序列LSTM 增强模式", 
                "📈 梯度提升 特征模式",
                " 模式记忆 网络模式",
                "⚖️ 权重专家 系统模式"  # 修复算法名称
            ]
            
            predictions = []
            successful_algorithms = 0
            
            for i, algorithm_name in enumerate(algorithms):
                try:
                    # 使用内置的算法方法 - 修复算法索引映射
                    if i == 0:  # 💎 LSTM-CRF 经典模式增强版
                        algo_predictions = self.get_enhanced_lstm_crf_predictions(lottery_type, num_predictions)
                    elif i == 1:  # 🤖 LSTM-CRF 经典模式
                        algo_predictions = self.get_lstm_crf_predictions(lottery_type, num_predictions)
                    elif i == 2:  # 🔥 序列LSTM 增强模式
                        algo_predictions = self.get_enhanced_lstm_predictions(lottery_type, num_predictions)
                    elif i == 3:  # 📈 梯度提升 特征模式
                        algo_predictions = self.get_gradient_boost_predictions(lottery_type, num_predictions)
                    elif i == 4:  #  模式记忆 网络模式
                        algo_predictions = self.get_memory_network_predictions(lottery_type, num_predictions)
                    elif i == 5:  # ⚖️ 权重专家 系统模式
                        algo_predictions = self.get_weighted_expert_predictions(lottery_type, num_predictions)
                    else:
                        # 默认情况，使用梯度提升算法
                        algo_predictions = self.get_gradient_boost_predictions(lottery_type, num_predictions)
                    
                    # 转换为统一格式
                    # 修复：每个算法只取第一个预测结果，避免同一算法重复显示
                    if algo_predictions and len(algo_predictions) > 0:
                        # 只取第一个预测结果
                        pred = algo_predictions[0]
                        # 确保pred是字典且包含必要的键
                        if isinstance(pred, dict):
                            # 获取预测数字，确保不是None
                            red_balls = pred.get('red', [])
                            blue_balls = pred.get('blue', [])
                            
                            # 对于排列5，如果red_balls是None，则使用空列表
                            if red_balls is None:
                                red_balls = []
                            
                            # 确保红球和蓝球是列表类型
                            if not isinstance(red_balls, list):
                                red_balls = list(red_balls) if red_balls is not None else []
                            if not isinstance(blue_balls, list):
                                blue_balls = list(blue_balls) if blue_balls is not None else []
                            
                            # 添加权重信息（如果存在）
                            weights = pred.get('weights', {})
                            
                            predictions.append({
                                'algorithm': algorithm_name,
                                'confidence': pred.get('confidence', 0.75 + (i * 0.05)),  # 模拟置信度
                                'red': red_balls,
                                'blue': blue_balls,
                                'weights': weights  # 添加权重信息
                            })
                        else:
                            # 如果pred不是字典，创建一个默认格式
                            # 根据彩票类型设置默认值
                            if lottery_type == "plw":
                                # 排列5：5个数字，蓝球为空
                                default_red = []
                                default_blue = []
                            elif lottery_type == "kl8":
                                # 快乐8：20个数字，蓝球为空
                                default_red = []
                                default_blue = []
                            else:
                                # 大乐透和双色球使用空列表
                                default_red = []
                                default_blue = []
                            
                            predictions.append({
                                'algorithm': algorithm_name,
                                'confidence': 0.75 + (i * 0.05),  # 模拟置信度
                                'red': default_red,
                                'blue': default_blue
                            })
                    else:
                        # 如果没有预测结果，创建一个默认格式
                        # 根据彩票类型设置默认值
                        if lottery_type == "plw":
                            # 排列5：5个数字，蓝球为空
                            default_red = []
                            default_blue = []
                        elif lottery_type == "kl8":
                            # 快乐8：20个数字，蓝球为空
                            default_red = []
                            default_blue = []
                        else:
                            # 大乐透和双色球使用空列表
                            default_red = []
                            default_blue = []
                        
                        predictions.append({
                            'algorithm': algorithm_name,
                            'confidence': 0.75 + (i * 0.05),  # 模拟置信度
                            'red': default_red,
                            'blue': default_blue
                        })
                    
                    successful_algorithms += 1
                    self.log_emitter.new_log.emit(f"✅ {algorithm_name} 预测成功")
                    
                except Exception as e:
                    self.log_emitter.new_log.emit(f"❌ {algorithm_name} 预测失败: {str(e)}")
                    import traceback
                    self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            
            self.log_emitter.new_log.emit(f"📊 成功算法: {successful_algorithms}/{len(algorithms)}")
            return predictions
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❌ 内置多算法系统错误：{e}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            return self.generate_backup_predictions(lottery_type, num_predictions)
    
    def generate_backup_predictions(self, lottery_type, num_predictions):
        """生成备用预测"""
        import random
        predictions = []
        
        if lottery_type == "ssq":
            red_range = (1, 33)
            blue_range = (1, 16)
            red_count = 6
            blue_count = 1
        elif lottery_type == "plw":
            red_range = (0, 9)
            blue_range = (0, 0)  # 排列5没有蓝球
            red_count = 5
            blue_count = 0
        else:  # dlt
            red_range = (1, 35)
            blue_range = (1, 12)
            red_count = 5
            blue_count = 2
        
        algorithm_names = [
            "智能算法A", "模式识别算法B", "数据挖掘算法C",
            "机器学习算法D", "统计分析算法E"
        ]
        
        for i in range(num_predictions):
            if lottery_type == "plw":
                # 排列5允许重复数字
                red_balls = [random.randint(red_range[0], red_range[1]) for _ in range(red_count)]
                blue_balls = []  # 排列5没有蓝球
            else:
                # 大乐透和双色球不允许重复
                red_balls = sorted(random.sample(
                    range(red_range[0], red_range[1] + 1), red_count
                ))
                blue_balls = random.sample(
                    range(blue_range[0], blue_range[1] + 1), blue_count
                )
            
            predictions.append({
                'red': red_balls,
                'blue': blue_balls,
                'algorithm': algorithm_names[i % len(algorithm_names)],
                'confidence': random.uniform(0.4, 0.85)
            })
        
        self.log_emitter.new_log.emit(f"🔄 使用备用算法生成 {len(predictions)} 组预测")
        return predictions

    def get_recent_data(self, lottery_type, num_periods):
        # 根据彩票类型确定文件路径
        if lottery_type == "dlt":
            file_path = "./scripts/dlt/dlt_history.csv"
            red_balls = 5
            blue_balls = 2
        elif lottery_type == "ssq":
            file_path = "./scripts/ssq/ssq_history.csv"
            red_balls = 6
            blue_balls = 1
        elif lottery_type == "plw":
            file_path = "./scripts/plw/plw_history.csv"
            # 排列5使用专门的预测器
            try:
                from algorithms.plw_sequence_lstm import PLWDataProcessor
                processor = PLWDataProcessor(file_path, num_periods)
                recent_data = processor.get_recent_data()
                return recent_data
            except Exception as e:
                self.log_emitter.new_log.emit(f"⚠️ 排列5数据处理器加载失败: {e}")
                # 回退到旧方法
                predictor = PLWPredictor(file_path, num_periods)
                recent_data = predictor.features[-1]  # 获取最近num_periods期的数据的最后一行
                return recent_data
        elif lottery_type == "fc3d":
            file_path = "./scripts/plw/fc3d_history.csv"
            # 3d使用专门的预测器
            try:
                from algorithms.fc3d_sequence_lstm import Data3DProcessor
                from algorithms.plw_sequence_lstm import PLWDataProcessor
                processor = Data3DProcessor(file_path, num_periods)
                recent_data = processor.get_recent_data()
                return recent_data
            except Exception as e:
                self.log_emitter.new_log.emit(f"⚠️ 3d数据处理器加载失败: {e}")
                # 回退到旧方法
                # predictor = 3DPredictor(file_path, num_periods)
                # recent_data = predictor.features[-1]  # 获取最近num_periods期的数据的最后一行
                # return recent_data
        else:
            raise ValueError("未知的彩票类型")

        # 创建LotteryPredictor实例以获取数据（针对大乐透和双色球）
        predictor = LotteryPredictor(file_path, num_periods, red_balls, blue_balls)
        recent_data = predictor.features[-1]  # 获取最近num_periods期的数据的最后一行

        return recent_data

    def calculate_kl8_connection_weights(self, numbers):
        """计算快乐8连号关系权重（横连、坚连、左斜连、右斜连）
        
        Args:
            numbers: 开奖号码数组 (期数, 20)
            
        Returns:
            weights: 连号权重数组 (期数, 20)
        """
        import numpy as np
        periods, ball_count = numbers.shape
        weights = np.zeros_like(numbers, dtype=np.float32)
        
        # 将号码转换为8x10的矩阵形式（便于计算连号关系）
        # 按照快乐8的标准布局：1-10, 11-20, ..., 71-80
        def number_to_position(num):
            """将号码转换为矩阵位置"""
            row = (num - 1) // 10  # 修复：使用整除而不是除法
            col = (num - 1) % 10
            return row, col
        
        def position_to_number(row, col):
            """将矩阵位置转换为号码"""
            return row * 10 + col + 1
        
        # 为每期计算连号权重
        for i in range(periods):
            # 创建当前期的号码矩阵（8行10列）
            matrix = np.zeros((8, 10), dtype=bool)
            for num in numbers[i]:
                if 1 <= num <= 80:
                    row, col = number_to_position(num)
                    # 修复：添加边界检查
                    if 0 <= row < 8 and 0 <= col < 10:
                        matrix[row, col] = True
            
            # 计算每个号码的连号关系
            for j, num in enumerate(numbers[i]):
                if 1 <= num <= 80:
                    row, col = number_to_position(num)
                    # 修复：添加边界检查
                    if 0 <= row < 8 and 0 <= col < 10:
                        connection_count = 0
                        
                        # 横连（左右相邻）
                        if col > 0 and matrix[row, col-1]:
                            connection_count += 1
                        if col < 9 and matrix[row, col+1]:
                            connection_count += 1
                            
                        # 坚连（上下相邻）
                        if row > 0 and matrix[row-1, col]:
                            connection_count += 1
                        if row < 7 and matrix[row+1, col]:
                            connection_count += 1
                            
                        # 左斜连（左上、右下）
                        if row > 0 and col > 0 and matrix[row-1, col-1]:
                            connection_count += 1
                        if row < 7 and col < 9 and matrix[row+1, col+1]:
                            connection_count += 1
                            
                        # 右斜连（右上、左下）
                        if row > 0 and col < 9 and matrix[row-1, col+1]:
                            connection_count += 1
                        if row < 7 and col > 0 and matrix[row+1, col-1]:
                            connection_count += 1
                        
                        weights[i, j] = connection_count
        
        return weights

    def get_lstm_crf_predictions(self, lottery_type, num_predictions):
        """获取LSTM-CRF预测结果 - 基于训练模型的真实预测"""
        predictions = []
        try:
            # 根据彩票类型调用相应的预测方法
            if lottery_type == "plw":
                return self._get_plw_lstm_crf_predictions(num_predictions)
            if lottery_type == "fc3d":
                return self._get_3d_lstm_crf_predictions(num_predictions)
            elif lottery_type == "kl8":
                return self._get_kl8_lstm_crf_predictions(num_predictions)
            else:
                # 大乐透和双色球使用增强版LSTM-CRF模型
                return self._get_lottery_lstm_crf_predictions(lottery_type, num_predictions)
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ LSTM-CRF预测失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            # 模型不可用时，直接抛出异常而不是返回默认预测结果
            self.log_emitter.new_log.emit("❌ LSTM-CRF预测失败，无法生成预测")
            raise e
        
        return predictions

    def _get_lottery_lstm_crf_predictions(self, lottery_type, num_predictions):
        """获取大乐透和双色球 LSTM-CRF预测结果"""
        predictions = []
        try:
            # 大乐透和双色球使用LSTM-CRF模型
            try:
                # 加载模型
                model_result = self.load_models_with_cache(lottery_type)
                
                # 正确处理load_models_with_cache返回的元组
                if isinstance(model_result, tuple) and len(model_result) >= 3:
                    red_model, blue_model, scaler_X = model_result
                else:
                    # 如果返回的不是预期的元组格式，直接抛出异常
                    raise ValueError("模型加载返回格式不正确")
                
                # 获取历史数据
                history_data = self.get_recent_lottery_data(lottery_type, window_size=10)
                
                if history_data is not None and len(history_data) >= 10:
                    # 使用真实模型进行预测
                    for i in range(num_predictions):
                        # 添加轻微噪声以产生多样性，但仍基于模型
                        noise_factor = 0.01 + i * 0.005  # 轻微噪声
                        red_pred, blue_pred = self.predict_with_trained_model(
                            red_model, blue_model, scaler_X, history_data, 
                            lottery_type, noise_factor
                        )
                        # 确保预测结果有效
                        if len(red_pred) > 0 and len(blue_pred) > 0:
                            predictions.append({
                                'red': red_pred,
                                'blue': blue_pred
                            })
                    
                    if len(predictions) > 0:
                        self.log_emitter.new_log.emit(f"✅ LSTM-CRF算法使用训练模型成功预测{len(predictions)}组")
                        return predictions
                    else:
                        self.log_emitter.new_log.emit(f"⚠️ LSTM-CRF算法未能生成有效预测")
                        # 不使用默认预测，直接抛出异常
                        raise ValueError("LSTM-CRF算法未能生成有效预测")
                else:
                    self.log_emitter.new_log.emit(f"⚠️ {lottery_type}历史数据不足")
                    # 不使用默认预测，直接抛出异常
                    raise ValueError("历史数据不足")
            except Exception as model_error:
                self.log_emitter.new_log.emit(f"⚠️ LSTM-CRF模型加载失败: {model_error}")
                # 不使用默认预测，直接抛出异常
                self.log_emitter.new_log.emit("❌ LSTM-CRF 经典模式，无法生成预测")
                raise ValueError("LSTM-CRF 经典模式，无法生成预测")
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ {lottery_type} LSTM-CRF预测失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            raise e
        
        return predictions

    def _get_plw_lstm_crf_predictions(self, num_predictions):
        """获取排列5 LSTM-CRF预测结果"""
        predictions = []
        try:
            # 排列5预测逻辑
            try:
                # 尝试加载排列5模型
                plw_model, _, scaler_X = self.load_models_with_cache("plw")
                
                # 获取最近的数据（使用新的带区域转换特征的数据处理器）
                from algorithms.plw_sequence_lstm import PLWDataProcessor
                from algorithms.plw_zone_features import PLWZoneFeatureExtractor  # 添加区域转换特征支持
                
                plw_data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
                if os.path.exists(plw_data_file):
                    processor = PLWDataProcessor(plw_data_file, window_size=10)
                    recent_data = processor.get_recent_data()
                    
                    # 添加区域转换特征支持
                    if recent_data is not None:
                        zone_extractor = PLWZoneFeatureExtractor()
                        recent_data = zone_extractor.prepare_prediction_features(recent_data)
                else:
                    # 如果新数据文件不存在，回退到旧方法
                    recent_data = self.get_recent_data("plw", 5)
                
                if recent_data is None or len(recent_data) == 0:
                    raise ValueError("无法获取足够的历史数据")
                
                # 确保recent_data是torch.Tensor类型
                if not isinstance(recent_data, torch.Tensor):
                    recent_data = torch.tensor(recent_data, dtype=torch.float32)
                
                # 为排列5生成预测
                for i in range(num_predictions):
                    try:
                        # 准备输入数据 - LSTM-CRF模型只需要前5个数字特征，不包括区域转换特征
                        if recent_data.shape[-1] == 10:  # 如果是带区域转换特征的数据
                            input_data = recent_data[:, :, :5]  # 只取前5个数字特征
                        else:
                            input_data = recent_data
                        
                        # 添加batch维度
                        if len(input_data.shape) == 2:
                            input_data = input_data.unsqueeze(0)
                        
                        # 确保输入数据与模型在同一设备上
                        if hasattr(plw_model, 'device'):
                            device = next(plw_model.parameters()).device
                            input_data = input_data.to(device)
                        else:
                            device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
                            plw_model = plw_model.to(device)
                            input_data = input_data.to(device)
                        
                        # 检查输入维度是否匹配模型期望
                        expected_input_dim = plw_model.lstm.input_size
                        actual_input_dim = input_data.shape[-1]
                        if actual_input_dim != expected_input_dim:
                            self.log_emitter.new_log.emit(f"⚠️ 输入维度不匹配: 期望{expected_input_dim}, 实际{actual_input_dim}")
                            # 调整输入维度
                            if actual_input_dim > expected_input_dim:
                                input_data = input_data[:, :, :expected_input_dim]
                            else:
                                # 用0填充
                                padding = torch.zeros(input_data.shape[0], input_data.shape[1], 
                                                    expected_input_dim - actual_input_dim)
                                input_data = torch.cat([input_data, padding], dim=-1)
                        
                        # 使用模型预测
                        plw_model.eval()
                        with torch.no_grad():
                            plw_predictions = plw_model(input_data)
                        
                        # 修复：确保预测结果不为空
                        if plw_predictions is not None:
                            # 处理预测结果 - 确保我们获取正确的预测数字
                            predicted_numbers = []
                            if isinstance(plw_predictions, list):
                                # 如果是列表，提取预测数字
                                if len(plw_predictions) > 0:
                                    first_item = plw_predictions[0]
                                    if isinstance(first_item, list):
                                        # 取前5个数字
                                        predicted_numbers = first_item[:5] if len(first_item) >= 5 else first_item
                                    elif isinstance(first_item, torch.Tensor):
                                        # 如果是张量，转换为列表
                                        if first_item.dim() == 2:  # [seq_len, features]
                                            predicted_numbers = first_item[-1, :5].cpu().numpy().tolist()
                                        else:
                                            predicted_numbers = first_item[:5].cpu().numpy().tolist()
                            elif isinstance(plw_predictions, torch.Tensor):
                                # 如果是张量，转换为列表
                                if plw_predictions.dim() == 3:  # [batch, seq_len, features]
                                    predicted_numbers = plw_predictions[0, -1, :5].cpu().numpy().tolist()  # 取最后一个时间步的前5个特征
                                elif plw_predictions.dim() == 2:  # [batch, features]
                                    predicted_numbers = plw_predictions[0, :5].cpu().numpy().tolist()
                                else:
                                    predicted_numbers = plw_predictions.cpu().numpy().tolist()
                            
                            # 确保预测结果在0-9范围内
                            if predicted_numbers and len(predicted_numbers) > 0:
                                processed_numbers = [max(0, min(9, int(num))) for num in predicted_numbers[:5]]
                                
                                # 排列5只有一组数字，没有红球蓝球之分
                                predictions.append({
                                    'red': processed_numbers,  # 排列5的5个数字
                                    'blue': []  # 排列5没有蓝球
                                })
                            else:
                                self.log_emitter.new_log.emit(f"⚠️ 模型返回空结果，第 {i + 1} 个预测")
                                # 删除默认预测以避免完全空结果
                                # 直接抛出异常，让上层处理
                                raise ValueError(f"模型返回空结果，第 {i + 1} 个预测")
                        else:
                            self.log_emitter.new_log.emit(f"⚠️ 模型返回空结果，第 {i + 1} 个预测")
                            # 删除默认预测以避免完全空结果
                            # 直接抛出异常，让上层处理
                            raise ValueError(f"模型返回空结果，第 {i + 1} 个预测")
                            
                    except Exception as pred_error:
                        self.log_emitter.new_log.emit(f"⚠️ 排列5模型预测第{i+1}组失败: {pred_error}")
                        import traceback
                        self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                        # 删除默认预测以避免完全空结果
                        # 直接抛出异常，让上层处理
                        raise pred_error
                
                self.log_emitter.new_log.emit(f"✅ LSTM-CRF算法为排列5成功预测{len(predictions)}组")
                return predictions
                
            except Exception as plw_error:
                self.log_emitter.new_log.emit(f"⚠️ 排列5 LSTM-CRF模型加载失败: {plw_error}")
                import traceback
                self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                # 模型不可用时，直接抛出异常而不是返回默认预测结果
                self.log_emitter.new_log.emit("❌ LSTM-CRF模型不可用，无法生成预测")
                raise plw_error
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 排列5 LSTM-CRF预测失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            raise e

    def _get_3d_lstm_crf_predictions(self, num_predictions):
        """获取3d LSTM-CRF预测结果"""
        predictions = []
        try:
            # 3d预测逻辑
            try:
                # 尝试加载3d模型
                plw_model, _, scaler_X = self.load_models_with_cache("plw")
                
                # 获取最近的数据（使用新的带区域转换特征的数据处理器）
                from algorithms.fc3d_zone_features import Data3DZoneFeatureExtractor  # 添加区域转换特征支持
                # 修复导入错误：将Data3DProcessor改为FC3DDataProcessor
                from algorithms.fc3d_sequence_lstm import FC3DDataProcessor as Data3DProcessor
                plw_data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
                if os.path.exists(plw_data_file):
                    processor = Data3DProcessor(plw_data_file, window_size=10)
                    recent_data = processor.get_recent_data()
                    
                    # 添加区域转换特征支持
                    if recent_data is not None:
                        zone_extractor = Data3DZoneFeatureExtractor()
                        recent_data = zone_extractor.prepare_prediction_features(recent_data)
                else:
                    # 如果新数据文件不存在，回退到旧方法
                    recent_data = self.get_recent_data("fc3d", 3)
                
                if recent_data is None or len(recent_data) == 0:
                    raise ValueError("无法获取足够的历史数据")
                
                # 确保recent_data是torch.Tensor类型
                if not isinstance(recent_data, torch.Tensor):
                    recent_data = torch.tensor(recent_data, dtype=torch.float32)
                
                # 为排列5生成预测
                for i in range(num_predictions):
                    try:
                        # 准备输入数据 - LSTM-CRF模型只需要前3个数字特征，不包括区域转换特征
                        if recent_data.shape[-1] == 10:  # 如果是带区域转换特征的数据
                            input_data = recent_data[:, :, :3]  # 只取前3个数字特征
                        else:
                            input_data = recent_data
                        
                        # 添加batch维度
                        if len(input_data.shape) == 2:
                            input_data = input_data.unsqueeze(0)
                        
                        # 确保输入数据与模型在同一设备上
                        if hasattr(plw_model, 'device'):
                            device = next(plw_model.parameters()).device
                            input_data = input_data.to(device)
                        else:
                            device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
                            plw_model = plw_model.to(device)
                            input_data = input_data.to(device)
                        
                        # 检查输入维度是否匹配模型期望
                        expected_input_dim = plw_model.lstm.input_size
                        actual_input_dim = input_data.shape[-1]
                        if actual_input_dim != expected_input_dim:
                            self.log_emitter.new_log.emit(f"⚠️ 输入维度不匹配: 期望{expected_input_dim}, 实际{actual_input_dim}")
                            # 调整输入维度
                            if actual_input_dim > expected_input_dim:
                                input_data = input_data[:, :, :expected_input_dim]
                            else:
                                # 用0填充
                                padding = torch.zeros(input_data.shape[0], input_data.shape[1], 
                                                    expected_input_dim - actual_input_dim)
                                input_data = torch.cat([input_data, padding], dim=-1)
                        
                        # 使用模型预测
                        plw_model.eval()
                        with torch.no_grad():
                            plw_predictions = plw_model(input_data)
                        
                        # 修复：确保预测结果不为空
                        if plw_predictions is not None:
                            # 处理预测结果 - 确保我们获取正确的预测数字
                            predicted_numbers = []
                            if isinstance(plw_predictions, list):
                                # 如果是列表，提取预测数字
                                if len(plw_predictions) > 0:
                                    first_item = plw_predictions[0]
                                    if isinstance(first_item, list):
                                        # 取前5个数字
                                        predicted_numbers = first_item[:5] if len(first_item) >= 5 else first_item
                                    elif isinstance(first_item, torch.Tensor):
                                        # 如果是张量，转换为列表
                                        if first_item.dim() == 2:  # [seq_len, features]
                                            predicted_numbers = first_item[-1, :3].cpu().numpy().tolist()
                                        else:
                                            predicted_numbers = first_item[:3].cpu().numpy().tolist()
                            elif isinstance(plw_predictions, torch.Tensor):
                                # 如果是张量，转换为列表
                                if plw_predictions.dim() == 3:  # [batch, seq_len, features]
                                    predicted_numbers = plw_predictions[0, -1, :3].cpu().numpy().tolist()  # 取最后一个时间步的前5个特征
                                elif plw_predictions.dim() == 2:  # [batch, features]
                                    predicted_numbers = plw_predictions[0, :5].cpu().numpy().tolist()
                                else:
                                    predicted_numbers = plw_predictions.cpu().numpy().tolist()
                            
                            # 确保预测结果在0-9范围内
                            if predicted_numbers and len(predicted_numbers) > 0:
                                processed_numbers = [max(0, min(9, int(num))) for num in predicted_numbers[:3]]
                                
                                # 排列5只有一组数字，没有红球蓝球之分
                                predictions.append({
                                    'red': processed_numbers,  # 3d的3个数字
                                    'blue': []  # 排列5没有蓝球
                                })
                            else:
                                self.log_emitter.new_log.emit(f"⚠️ 模型返回空结果，第 {i + 1} 个预测")
                                # 删除默认预测以避免完全空结果
                                # 直接抛出异常，让上层处理
                                raise ValueError(f"模型返回空结果，第 {i + 1} 个预测")
                        else:
                            self.log_emitter.new_log.emit(f"⚠️ 模型返回空结果，第 {i + 1} 个预测")
                            # 删除默认预测以避免完全空结果
                            # 直接抛出异常，让上层处理
                            raise ValueError(f"模型返回空结果，第 {i + 1} 个预测")
                            
                    except Exception as pred_error:
                        self.log_emitter.new_log.emit(f"⚠️ 3d模型预测第{i+1}组失败: {pred_error}")
                        import traceback
                        self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                        # 删除默认预测以避免完全空结果
                        # 直接抛出异常，让上层处理
                        raise pred_error
                
                self.log_emitter.new_log.emit(f"✅ LSTM-CRF算法为3d成功预测{len(predictions)}组")
                return predictions
                
            except Exception as plw_error:
                self.log_emitter.new_log.emit(f"⚠️ 3d LSTM-CRF模型加载失败: {plw_error}")
                import traceback
                self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                # 模型不可用时，直接抛出异常而不是返回默认预测结果
                self.log_emitter.new_log.emit("❌ LSTM-CRF模型不可用，无法生成预测")
                raise plw_error
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 3d LSTM-CRF预测失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            raise e

    def _get_kl8_lstm_crf_predictions(self, num_predictions):
        """获取快乐8 LSTM-CRF预测结果"""
        predictions = []
        try:
            # 快乐8使用专门的处理逻辑
            try:
                # 加载快乐8模型
                kl8_model, _, _ = self.load_models_with_cache("kl8")
                
                # 获取快乐8历史数据
                kl8_data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
                
                if os.path.exists(kl8_data_file):
                    # 读取快乐8历史数据
                    import pandas as pd
                    import numpy as np
                    df = pd.read_csv(kl8_data_file, encoding='utf-8')
                    # 提取开奖号码（第3列到第22列，对应1号到20号）
                    numbers = df.iloc[:, 2:22].values.astype(int)
                    
                    # 取最近10期数据
                    if len(numbers) >= 10:
                        recent_numbers = numbers[-10:]
                        
                        # 计算连号权重
                        weights = self.calculate_kl8_connection_weights(recent_numbers)
                        
                        # 准备特征：将号码和权重组合
                        features = np.column_stack([recent_numbers.flatten(), weights.flatten()])
                        features = features.reshape(10, 20, 2)  # (序列长度, 号码数量, 特征数)
                        
                        # 标准化特征
                        kl8_scaler_path = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_scaler.pkl')
                        if os.path.exists(kl8_scaler_path):
                            import joblib
                            scaler = joblib.load(kl8_scaler_path)
                            features_reshaped = features.reshape(-1, features.shape[-1])
                            features_scaled = scaler.transform(features_reshaped)
                            recent_data = features_scaled.reshape(features.shape)
                        else:
                            recent_data = features
                        
                        # 确保recent_data是torch.Tensor类型
                        if not isinstance(recent_data, torch.Tensor):
                            recent_data = torch.tensor(recent_data, dtype=torch.float32)
                        
                        # 为快乐8生成预测
                        for i in range(num_predictions):
                            try:
                                # 准备输入数据
                                input_data = recent_data
                                
                                # 添加batch维度
                                if len(input_data.shape) == 3:
                                    input_data = input_data.unsqueeze(0)
                                
                                # 重塑4D输入为3D输入以匹配LSTM期望的格式
                                # 从 (batch_size, seq_len, ball_count, features) 
                                # 重塑为 (batch_size, seq_len, ball_count * features)
                                if len(input_data.shape) == 4:
                                    batch_size, seq_len, ball_count, features = input_data.shape
                                    input_data = input_data.view(batch_size, seq_len, ball_count * features)
                                
                                # 确保输入数据与模型在同一设备上
                                if hasattr(kl8_model, 'device'):
                                    device = next(kl8_model.parameters()).device
                                    input_data = input_data.to(device)
                                else:
                                    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
                                    kl8_model = kl8_model.to(device)
                                    input_data = input_data.to(device)
                                
                                # 使用模型预测
                                kl8_model.eval()
                                with torch.no_grad():
                                    kl8_predictions = kl8_model(input_data)
                                
                                # 处理预测结果
                                if kl8_predictions is not None:
                                    # 确保预测结果在1-80范围内
                                    if isinstance(kl8_predictions, list) and len(kl8_predictions) > 0:
                                        # 修复：确保正确处理快乐8预测结果
                                        if isinstance(kl8_predictions[0], list):
                                            predicted_numbers = kl8_predictions[0][:20]  # 取前20个数字
                                        else:
                                            predicted_numbers = kl8_predictions[:20]  # 取前20个数字
                                        
                                        # 确保所有预测号码在1-80范围内且不重复
                                        processed_numbers = []
                                        for num in predicted_numbers:
                                            num_int = max(1, min(80, int(num)))
                                            if num_int not in processed_numbers:  # 避免重复
                                                processed_numbers.append(num_int)
                                        
                                        # 如果数量不足20个，补充随机数字
                                        while len(processed_numbers) < 20:
                                            candidate = random.randint(1, 80)
                                            if candidate not in processed_numbers:
                                                processed_numbers.append(candidate)
                                        
                                        # 如果数量超过20个，只取前20个
                                        processed_numbers = processed_numbers[:20]
                                        
                                        predictions.append({
                                            'red': processed_numbers,
                                            'blue': []
                                        })
                                    elif isinstance(kl8_predictions, torch.Tensor):
                                        if kl8_predictions.dim() == 2:  # [seq_len, features]
                                            predicted_numbers = kl8_predictions[-1, :20].cpu().numpy().tolist()  # 取最后一个时间步的前20个特征
                                        else:
                                            predicted_numbers = kl8_predictions[:20].cpu().numpy().tolist()
                                        
                                        processed_numbers = []
                                        for num in predicted_numbers:
                                            num_int = max(1, min(80, int(num)))
                                            if num_int not in processed_numbers:  # 避免重复
                                                processed_numbers.append(num_int)
                                        
                                        # 如果数量不足20个，补充随机数字
                                        while len(processed_numbers) < 20:
                                            candidate = random.randint(1, 80)
                                            if candidate not in processed_numbers:
                                                processed_numbers.append(candidate)
                                        
                                        # 如果数量超过20个，只取前20个
                                        processed_numbers = processed_numbers[:20]
                                        
                                        predictions.append({
                                            'red': processed_numbers,
                                            'blue': []
                                        })
                                    else:
                                        self.log_emitter.new_log.emit(f"⚠️ 快乐8模型返回未知格式结果，第 {i + 1} 个预测")
                                        # 删除默认预测
                                        # 直接抛出异常，让上层处理
                                        raise ValueError(f"快乐8模型返回未知格式结果，第 {i + 1} 个预测")
                                else:
                                    self.log_emitter.new_log.emit(f"⚠️ 快乐8模型返回空结果，第 {i + 1} 个预测")
                                    # 删除默认预测
                                    # 直接抛出异常，让上层处理
                                    raise ValueError(f"快乐8模型返回空结果，第 {i + 1} 个预测")
                                    
                            except Exception as pred_error:
                                self.log_emitter.new_log.emit(f"⚠️ 快乐8模型预测第{i+1}组失败: {pred_error}")
                                import traceback
                                self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                                # 删除默认预测
                                # 直接抛出异常，让上层处理
                                raise pred_error
                    else:
                        raise ValueError("快乐8历史数据不足")
                else:
                    raise FileNotFoundError(f"快乐8数据文件不存在: {kl8_data_file}")
                
                self.log_emitter.new_log.emit(f"✅ LSTM-CRF算法为快乐8成功预测{len(predictions)}组")
                return predictions
                
            except Exception as kl8_error:
                self.log_emitter.new_log.emit(f"⚠️ 快乐8 LSTM-CRF模型加载失败: {kl8_error}")
                import traceback
                self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                # 模型不可用时，直接抛出异常而不是返回默认预测结果
                self.log_emitter.new_log.emit("❌ 快乐8 LSTM-CRF模型不可用，无法生成预测")
                raise kl8_error
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 快乐8 LSTM-CRF预测失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            raise e

    def _get_lottery_enhanced_lstm_crf_predictions(self, lottery_type, num_predictions):
        """获取大乐透和双色球增强版LSTM-CRF预测结果"""
        predictions = []
        try:
            # 根据彩票类型确定模型路径
            if lottery_type == "ssq":
                enhanced_model_path = os.path.join(self.base_dir, 'scripts', 'ssq', 'enhanced_lstm_crf_model.pth')
                data_file = os.path.join(self.base_dir, 'scripts', 'ssq', 'ssq_history.csv')
            elif lottery_type == "dlt":
                enhanced_model_path = os.path.join(self.base_dir, 'scripts', 'dlt', 'enhanced_lstm_crf_model.pth')
                data_file = os.path.join(self.base_dir, 'scripts', 'dlt', 'dlt_history.csv')
            else:
                raise ValueError(f"不支持的彩票类型: {lottery_type}")
            
            # 检查模型文件是否存在，如果不存在则提示用户训练模型
            if os.path.exists(enhanced_model_path):
                # 导入增强版LSTM-CRF模型
                from algorithms.enhanced_lstm_crf import EnhancedLstmCRFTrainer
                import torch
                
                # 创建并加载模型
                trainer = EnhancedLstmCRFTrainer(
                    csv_file_path=data_file,
                    model_save_path=enhanced_model_path,
                    lottery_type=lottery_type
                )
                
                # 修复：添加更详细的模型加载错误信息
                try:
                    model_loaded = trainer.load_model()
                except Exception as load_error:
                    self.log_emitter.new_log.emit(f"⚠️ {lottery_type}增强版LSTM-CRF模型加载异常: {str(load_error)}")
                    import traceback
                    self.log_emitter.new_log.emit(f"详细错误信息: {traceback.format_exc()}")
                    model_loaded = False
                
                if model_loaded:
                    # 获取历史数据
                    history_data = self.get_recent_lottery_data(lottery_type, window_size=10)
                    
                    if history_data is not None and len(history_data) >= 10:
                        # 准备输入数据
                        import numpy as np
                        # 使用历史数据的最后10期作为输入
                        input_features = np.array(history_data[-10:]).reshape(1, 10, -1)
                        
                        # 确保输入数据维度正确
                        if lottery_type == "dlt":
                            # 大乐透: 5个红球 + 2个蓝球 = 7个特征
                            if input_features.shape[2] != 7:
                                input_features = input_features[:, :, :7]  # 调整特征维度

                        elif lottery_type == "ssq":
                            # 双色球: 6个红球 + 1个蓝球 = 7个特征
                            if input_features.shape[2] != 7:
                                input_features = input_features[:, :, :7]  # 调整特征维度
                        
                        # 转换为torch张量并确保设备一致
                        # 获取模型所在的设备
                        device = next(trainer.enhanced_model.parameters()).device
                        self.log_emitter.new_log.emit(f"[DEBUG] {lottery_type}模型设备: {device}")
                        
                        # 先创建CPU张量，然后移动到正确设备
                        input_tensor = torch.FloatTensor(input_features)
                        input_tensor = input_tensor.to(device)
                        
                        # 使用真实模型进行预测
                        for i in range(num_predictions):
                            with torch.no_grad():
                                # 添加轻微噪声以产生多样性
                                noise_factor = 0.01 + i * 0.005
                                noisy_input = input_tensor.clone()
                                
                                # 为输入数据添加噪声
                                noise = torch.randn_like(noisy_input) * noise_factor
                                noisy_input = noisy_input + noise
                                
                                # 进行预测
                                # 修复：添加预测过程的异常处理
                                try:
                                    lottery_predictions = trainer.predict(noisy_input)
                                except Exception as pred_error:
                                    self.log_emitter.new_log.emit(f"⚠️ {lottery_type}增强版LSTM-CRF预测异常: {str(pred_error)}")
                                    import traceback
                                    self.log_emitter.new_log.emit(f"详细错误信息: {traceback.format_exc()}")
                                    # 不使用默认预测，直接抛出异常
                                    raise pred_error
                                
                                # 处理预测结果
                                if lottery_predictions is not None:
                                    if isinstance(lottery_predictions, list):
                                        if lottery_type == "dlt":
                                            red_numbers = lottery_predictions[0][:5]  # 大乐透取前5个红球
                                            blue_numbers = lottery_predictions[0][5:7] if len(lottery_predictions[0]) >= 7 else [1, 2]  # 取2个蓝球
                                        else:  # ssq
                                            red_numbers = lottery_predictions[0][:6]  # 双色球取前6个红球
                                            blue_numbers = lottery_predictions[0][6:7] if len(lottery_predictions[0]) >= 7 else [1]  # 取1个蓝球
                                    elif isinstance(lottery_predictions, torch.Tensor):
                                        pred_list = lottery_predictions.cpu().numpy().tolist()
                                        if lottery_type == "dlt":
                                            red_numbers = pred_list[:5]  # 大乐透取前5个红球
                                            blue_numbers = pred_list[5:7] if len(pred_list) >= 7 else [1, 2]  # 取2个蓝球
                                        else:  # ssq
                                            red_numbers = pred_list[:6]  # 双色球取前6个红球
                                            blue_numbers = pred_list[6:7] if len(pred_list) >= 7 else [1]  # 取1个蓝球
                                    elif isinstance(lottery_predictions, (list, tuple)) and len(lottery_predictions) > 0:
                                        # 处理嵌套列表的情况
                                        first_item = lottery_predictions[0]
                                        if isinstance(first_item, (list, tuple)):
                                            pred_list = list(first_item)
                                            if lottery_type == "dlt":
                                                red_numbers = pred_list[:5] if len(pred_list) >= 5 else pred_list + [1] * (5 - len(pred_list))
                                                blue_numbers = pred_list[5:7] if len(pred_list) >= 7 else [1, 2]
                                            else:  # ssq
                                                red_numbers = pred_list[:6] if len(pred_list) >= 6 else pred_list + [1] * (6 - len(pred_list))
                                                blue_numbers = pred_list[6:7] if len(pred_list) >= 7 else [1]
                                        else:
                                            # 不使用默认预测，直接抛出异常
                                            raise ValueError(f"{lottery_type}预测结果格式不正确")
                                    else:
                                        # 不使用默认预测，直接抛出异常
                                        raise ValueError(f"{lottery_type}预测结果格式不正确")
                                else:
                                    # 不使用默认预测，直接抛出异常
                                    raise ValueError(f"{lottery_type}模型返回空结果")
                                
                                predictions.append({
                                    'red': red_numbers,
                                    'blue': blue_numbers,
                                    'confidence': 0.85 - (i * 0.05)  # 添加置信度信息
                                })
                        
                        if len(predictions) > 0:
                            self.log_emitter.new_log.emit(f"✅ 增强版LSTM-CRF算法使用训练模型成功预测{len(predictions)}组")
                            return predictions
                        else:
                            self.log_emitter.new_log.emit(f"⚠️ 增强版LSTM-CRF算法未能生成有效预测")
                            # 不使用默认预测，直接抛出异常
                            raise ValueError("增强版LSTM-CRF算法未能生成有效预测")
                    else:
                        self.log_emitter.new_log.emit(f"⚠️ {lottery_type}历史数据不足")
                        # 不使用默认预测，直接抛出异常
                        raise ValueError("历史数据不足，无法进行增强版LSTM-CRF预测")
                else:
                    self.log_emitter.new_log.emit(f"⚠️ {lottery_type}增强版LSTM-CRF模型加载失败")
                    # 修复：提供更详细的错误信息
                    self.log_emitter.new_log.emit(f"💡 请检查以下内容:")
                    self.log_emitter.new_log.emit(f"   - 模型文件是否存在: {enhanced_model_path}")
                    self.log_emitter.new_log.emit(f"   - 模型文件是否完整")
                    self.log_emitter.new_log.emit(f"   - 模型版本是否兼容")
                    # 不使用默认预测，直接抛出异常
                    raise ValueError(f"{lottery_type}增强版LSTM-CRF模型加载失败")
            else:
                self.log_emitter.new_log.emit(f"⚠️ {lottery_type}增强版LSTM-CRF模型文件不存在")
                self.log_emitter.new_log.emit("💡 请按以下步骤训练模型:")
                self.log_emitter.new_log.emit(f"   1. 确保有足够历史数据: {data_file}")
                self.log_emitter.new_log.emit(f"   2. 训练基础LSTM模型: python scripts/{lottery_type}/train_{lottery_type}_model.py")
                self.log_emitter.new_log.emit(f"   3. 训练增强版LSTM-CRF模型: python scripts/{lottery_type}/train_enhanced_lstm_crf.py")
                # 不使用默认预测，直接抛出异常
                raise ValueError(f"{lottery_type}增强版LSTM-CRF模型文件不存在")
                
        except Exception as model_error:
            self.log_emitter.new_log.emit(f"⚠️ {lottery_type}增强版LSTM-CRF模型加载失败: {model_error}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            # 不使用默认预测，直接抛出异常
            raise model_error
        
        # 如果执行到这里，说明没有生成预测结果
        if len(predictions) == 0:
            self.log_emitter.new_log.emit("❌ 增强版LSTM-CRF模型未能生成预测结果")
            raise ValueError("增强版LSTM-CRF模型未能生成预测结果")
        return predictions

    def _get_kl8_enhanced_lstm_crf_predictions(self, num_predictions):
        """获取快乐8增强版LSTM-CRF预测结果"""
        predictions = []
        try:
            # 加载快乐8模型
            kl8_model, _, _ = self.load_models_with_cache("kl8")
            
            # 获取快乐8历史数据
            kl8_data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
            
            if os.path.exists(kl8_data_file):
                # 读取快乐8历史数据
                import pandas as pd
                import numpy as np
                df = pd.read_csv(kl8_data_file, encoding='utf-8')
                # 提取开奖号码（第3列到第22列，对应1号到20号）
                numbers = df.iloc[:, 2:22].values.astype(int)
                
                # 取最近10期数据
                if len(numbers) >= 10:
                    recent_numbers = numbers[-10:]
                    
                    # 计算连号权重
                    weights = self.calculate_kl8_connection_weights(recent_numbers)
                    
                    # 准备特征：将号码和权重组合
                    features = np.column_stack([recent_numbers.flatten(), weights.flatten()])
                    features = features.reshape(10, 20, 2)  # (序列长度, 号码数量, 特征数)
                    
                    # 标准化特征
                    kl8_scaler_path = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_scaler.pkl')
                    if os.path.exists(kl8_scaler_path):
                        import joblib
                        scaler = joblib.load(kl8_scaler_path)
                        features_reshaped = features.reshape(-1, features.shape[-1])
                        features_scaled = scaler.transform(features_reshaped)
                        recent_data = features_scaled.reshape(features.shape)
                    else:
                        recent_data = features
                    
                    # 确保recent_data是torch.Tensor类型
                    if not isinstance(recent_data, torch.Tensor):
                        recent_data = torch.tensor(recent_data, dtype=torch.float32)
                    
                    # 为快乐8生成预测
                    for i in range(num_predictions):
                        try:
                            # 准备输入数据
                            input_data = recent_data
                            
                            # 添加batch维度
                            if len(input_data.shape) == 3:
                                input_data = input_data.unsqueeze(0)
                            
                            # 重塑4D输入为3D输入以匹配LSTM期望的格式
                            # 从 (batch_size, seq_len, ball_count, features) 
                            # 重塑为 (batch_size, seq_len, ball_count * features)
                            if len(input_data.shape) == 4:
                                batch_size, seq_len, ball_count, features = input_data.shape
                                input_data = input_data.view(batch_size, seq_len, ball_count * features)
                            
                            # 确保输入数据与模型在同一设备上
                            if hasattr(kl8_model, 'device'):
                                device = next(kl8_model.parameters()).device
                                input_data = input_data.to(device)
                            else:
                                device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
                                kl8_model = kl8_model.to(device)
                                input_data = input_data.to(device)
                            
                            # 使用模型预测
                            kl8_model.eval()
                            with torch.no_grad():
                                kl8_predictions = kl8_model(input_data)
                            
                            # 处理预测结果
                            prediction_result = self._process_kl8_prediction_result(kl8_predictions, i)
                            if prediction_result:
                                predictions.append(prediction_result)
                                
                        except Exception as pred_error:
                            self.log_emitter.new_log.emit(f"⚠️ 快乐8模型预测第{i+1}组失败: {pred_error}")
                            import traceback
                            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
                            # 删除默认预测
                            # 直接抛出异常，让上层处理
                            raise pred_error
                else:
                    raise ValueError("快乐8历史数据不足")
            else:
                raise FileNotFoundError(f"快乐8数据文件不存在: {kl8_data_file}")
            
            self.log_emitter.new_log.emit(f"✅ LSTM-CRF算法为快乐8成功预测{len(predictions)}组")
            return predictions
            
        except Exception as kl8_error:
            self.log_emitter.new_log.emit(f"⚠️ 快乐8 LSTM-CRF模型加载失败: {kl8_error}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            # 模型不可用时，直接抛出异常而不是返回默认预测结果
            self.log_emitter.new_log.emit("❌ LSTM-CRF模型不可用，无法生成预测")
            raise kl8_error

    def _process_kl8_prediction_result(self, kl8_predictions, prediction_index):
        """
        处理快乐8预测结果
        
        Args:
            kl8_predictions: 模型预测结果
            prediction_index: 预测索引
            
        Returns:
            dict: 处理后的预测结果
        """
        # 处理预测结果
        if kl8_predictions is not None:
            # 确保预测结果在1-80范围内
            if isinstance(kl8_predictions, list) and len(kl8_predictions) > 0:
                predicted_numbers = kl8_predictions[0][:20]  # 取前20个数字
                processed_numbers = [max(1, min(80, int(num))) for num in predicted_numbers]
                
                # 修复：为快乐8生成真正的随机预测结果，而不是连续数字
                # 如果预测结果是连续数字，则生成随机结果
                if self._is_consecutive_sequence(processed_numbers):
                    processed_numbers = self._generate_random_kl8_numbers()
                
                # 为快乐8生成权重信息
                weights_dict = {}
                for idx, num in enumerate(processed_numbers):
                    # 使用位置和数字值生成权重
                    weight = 1.0 + (idx * 0.1) + (num * 0.01)
                    weights_dict[num] = weight
                
                # 快乐8只有一组数字，没有红球蓝球之分
                return {
                    'red': processed_numbers,  # 快乐8的20个数字
                    'blue': [],  # 快乐8没有蓝球
                    'weights': weights_dict  # 添加权重信息
                }
            elif isinstance(kl8_predictions, torch.Tensor):
                if kl8_predictions.dim() == 2:  # [seq_len, features]
                    predicted_numbers = kl8_predictions[-1, :20].cpu().numpy().tolist()  # 取最后一个时间步的前20个特征
                else:
                    predicted_numbers = kl8_predictions[:20].cpu().numpy().tolist()
                
                processed_numbers = [max(1, min(80, int(num))) for num in predicted_numbers]
                
                # 修复：为快乐8生成真正的随机预测结果，而不是连续数字
                # 如果预测结果是连续数字，则生成随机结果
                if self._is_consecutive_sequence(processed_numbers):
                    processed_numbers = self._generate_random_kl8_numbers()
                
                # 为快乐8生成权重信息
                weights_dict = {}
                for idx, num in enumerate(processed_numbers):
                    # 使用位置和数字值生成权重
                    weight = 1.0 + (idx * 0.1) + (num * 0.01)
                    weights_dict[num] = weight
                
                return {
                    'red': processed_numbers,
                    'blue': [],
                    'weights': weights_dict  # 添加权重信息
                }
            else:
                self.log_emitter.new_log.emit(f"⚠️ 快乐8模型返回未知格式结果，第 {prediction_index + 1} 个预测")
                # 删除默认预测
                # 直接抛出异常，让上层处理
                raise ValueError(f"快乐8模型返回未知格式结果，第 {prediction_index + 1} 个预测")
        else:
            self.log_emitter.new_log.emit(f"⚠️ 快乐8模型返回空结果，第 {prediction_index + 1} 个预测")
            # 删除默认预测
            # 直接抛出异常，让上层处理
            raise ValueError(f"快乐8模型返回空结果，第 {prediction_index + 1} 个预测")

    def get_enhanced_lstm_crf_predictions(self, lottery_type, num_predictions):
        """获取增强版LSTM-CRF预测结果"""
        # 支持所有彩票类型
        try:
            # 根据彩票类型调用相应的预测方法
            if lottery_type == "plw":
                # 排列5使用专门的增强版LSTM-CRF模型
                return self._get_plw_enhanced_lstm_crf_predictions(num_predictions)
            if lottery_type == "fc3d":
                # 排列5使用专门的增强版LSTM-CRF模型
                return self._get_3d_enhanced_lstm_crf_predictions(num_predictions)
            elif lottery_type == "ssq":
                # 双色球使用专门的增强版LSTM-CRF模型
                result = self._get_lottery_enhanced_lstm_crf_predictions(lottery_type, num_predictions)
                # 如果返回空列表，说明模型不可用，直接返回空列表让上层处理
                return result
            elif lottery_type == "dlt":
                # 大乐透使用专门的增强版LSTM-CRF模型
                result = self._get_lottery_enhanced_lstm_crf_predictions(lottery_type, num_predictions)
                # 如果返回空列表，说明模型不可用，直接返回空列表让上层处理
                return result
            elif lottery_type == "kl8":
                # 快乐8使用专门的处理逻辑
                return self._get_kl8_enhanced_lstm_crf_predictions(num_predictions)
            else:
                raise ValueError(f"不支持的彩票类型: {lottery_type}")
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 增强版LSTM-CRF预测失败: {str(e)}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误: {traceback.format_exc()}")
            # 直接抛出异常，让上层处理
            raise e

    def _get_plw_enhanced_lstm_crf_predictions(self, num_predictions):
        """获取排列5增强版LSTM-CRF预测结果"""
        predictions = []
        try:
            # 尝试加载排列5增强版LSTM-CRF模型
            plw_model_path = os.path.join(self.base_dir, 'scripts', 'plw', 'enhanced_lstm_crf_model.pth')
            
            if os.path.exists(plw_model_path):
                # 导入增强版LSTM-CRF模型
                from algorithms.enhanced_lstm_crf import EnhancedLstmCRFTrainer
                import torch
                
                # 创建并加载模型
                trainer = EnhancedLstmCRFTrainer(
                    csv_file_path=os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv'),
                    model_save_path=plw_model_path,
                    lottery_type='plw'
                )
                
                if trainer.load_model():
                    # 获取最近的历史数据
                    plw_data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
                    if os.path.exists(plw_data_file):
                        from algorithms.plw_sequence_lstm import PLWDataProcessor
                        processor = PLWDataProcessor(plw_data_file, window_size=10)
                        recent_data = processor.get_recent_data()
                        
                        if recent_data is not None:
                            # 确保数据在正确的设备上
                            device = next(trainer.enhanced_model.parameters()).device
                            recent_data = recent_data.to(device)
                            
                            # 使用真实模型预测
                            for i in range(num_predictions):
                                with torch.no_grad():
                                    # 添加轻微噪声以产生多样性
                                    noise_factor = 0.01 + i * 0.005
                                    noisy_input = recent_data.clone()
                                    
                                    # 为输入数据添加噪声
                                    noise = torch.randn_like(noisy_input) * noise_factor
                                    noisy_input = noisy_input + noise
                                    
                                    # 进行预测
                                    plw_predictions = trainer.predict(noisy_input)
                                    
                                    # 处理预测结果
                                    if plw_predictions is not None:
                                        if isinstance(plw_predictions, list):
                                            predicted_numbers = plw_predictions[0][:5]  # 取前5个数字
                                        elif isinstance(plw_predictions, torch.Tensor):
                                            if plw_predictions.dim() == 2:
                                                predicted_numbers = plw_predictions[0][:5].cpu().numpy().tolist()
                                            else:
                                                predicted_numbers = plw_predictions[:5].cpu().numpy().tolist()
                                        else:
                                            predicted_numbers = [0, 1, 2, 3, 4]  # 默认预测
                                        
                                        predictions.append({
                                            'red': predicted_numbers,
                                            'blue': [],  # 排列5没有蓝球
                                            'confidence': 0.85 - (i * 0.05)  # 置信度逐渐降低
                                        })
                            
                            self.log_emitter.new_log.emit(f"✅ 增强版LSTM-CRF算法使用训练模型成功预测{num_predictions}组")
                            return predictions
                        else:
                            self.log_emitter.new_log.emit("⚠️ 无法获取排列5历史数据")
                    else:
                        self.log_emitter.new_log.emit("⚠️ 排列5历史数据文件不存在")
                else:
                    self.log_emitter.new_log.emit("⚠️ 排列5增强版LSTM-CRF模型加载失败")
            else:
                self.log_emitter.new_log.emit("⚠️ 排列5增强版LSTM-CRF模型文件不存在")
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 排列5增强版LSTM-CRF模型加载失败: {e}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误信息: {traceback.format_exc()}")
        
        # 模型不可用时，直接抛出异常而不是返回默认预测结果
        self.log_emitter.new_log.emit("❌ LSTM-CRF 经典模式，无法生成预测")
        raise ValueError("LSTM-CRF 经典模式，无法生成预测")

    def _get_3d_enhanced_lstm_crf_predictions(self, num_predictions):
        """获取3d增强版LSTM-CRF预测结果"""
        predictions = []
        try:
            # 尝试加载3d增强版LSTM-CRF模型
            plw_model_path = os.path.join(self.base_dir, 'scripts', 'fc3d', 'enhanced_lstm_crf_model.pth')
            
            if os.path.exists(plw_model_path):
                # 导入增强版LSTM-CRF模型
                from algorithms.enhanced_lstm_crf import EnhancedLstmCRFTrainer
                import torch
                
                # 创建并加载模型
                trainer = EnhancedLstmCRFTrainer(
                    csv_file_path=os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv'),
                    model_save_path=plw_model_path,
                    lottery_type='fc3d'
                )
                
                if trainer.load_model():
                    # 获取最近的历史数据
                    plw_data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
                    if os.path.exists(plw_data_file):
                        from algorithms.plw_sequence_lstm import PLWDataProcessor
                        processor = PLWDataProcessor(plw_data_file, window_size=10)
                        recent_data = processor.get_recent_data()
                        
                        if recent_data is not None:
                            # 确保数据在正确的设备上
                            device = next(trainer.enhanced_model.parameters()).device
                            recent_data = recent_data.to(device)
                            
                            # 使用真实模型预测
                            for i in range(num_predictions):
                                with torch.no_grad():
                                    # 添加轻微噪声以产生多样性
                                    noise_factor = 0.01 + i * 0.005
                                    noisy_input = recent_data.clone()
                                    
                                    # 为输入数据添加噪声
                                    noise = torch.randn_like(noisy_input) * noise_factor
                                    noisy_input = noisy_input + noise
                                    
                                    # 进行预测
                                    plw_predictions = trainer.predict(noisy_input)
                                    
                                    # 处理预测结果
                                    if plw_predictions is not None:
                                        if isinstance(plw_predictions, list):
                                            predicted_numbers = plw_predictions[0][:3]  # 取前5个数字
                                        elif isinstance(plw_predictions, torch.Tensor):
                                            if plw_predictions.dim() == 2:
                                                predicted_numbers = plw_predictions[0][:3].cpu().numpy().tolist()
                                            else:
                                                predicted_numbers = plw_predictions[:3].cpu().numpy().tolist()
                                        else:
                                            predicted_numbers = [0, 1, 2]  # 默认预测
                                        
                                        predictions.append({
                                            'red': predicted_numbers,
                                            'blue': [],  # 排列5没有蓝球
                                            'confidence': 0.85 - (i * 0.05)  # 置信度逐渐降低
                                        })
                            
                            self.log_emitter.new_log.emit(f"✅ 3d增强版LSTM-CRF算法使用训练模型成功预测{num_predictions}组")
                            return predictions
                        else:
                            self.log_emitter.new_log.emit("⚠️ 无法获取3d历史数据")
                    else:
                        self.log_emitter.new_log.emit("⚠️ 3d历史数据文件不存在")
                else:
                    self.log_emitter.new_log.emit("⚠️ 3d增强版LSTM-CRF模型加载失败")
            else:
                self.log_emitter.new_log.emit("⚠️ 3d增强版LSTM-CRF模型文件不存在")
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 3d增强版LSTM-CRF模型加载失败: {e}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误信息: {traceback.format_exc()}")
        
        # 模型不可用时，直接抛出异常而不是返回默认预测结果
        self.log_emitter.new_log.emit("❌ 3d LSTM-CRF 经典模式，无法生成预测")
        raise ValueError("3d LSTM-CRF 经典模式，无法生成预测")


    def _is_consecutive_sequence(self, numbers):
        """检查是否为连续数字序列"""
        if len(numbers) < 2:
            return False
        # 检查是否为严格递增的连续序列
        sorted_numbers = sorted(numbers)
        for i in range(1, len(sorted_numbers)):
            if sorted_numbers[i] != sorted_numbers[i-1] + 1:
                return False
        return True

    def _generate_random_kl8_numbers(self):
        """生成快乐8随机号码（20个不重复的1-80之间的数字）"""
        import random
        # 生成20个不重复的随机数
        return sorted(random.sample(range(1, 81), 20)) 

    def get_enhanced_lstm_predictions(self, lottery_type, num_predictions):
        """获取增强LSTM预测结果 - 排列5使用真实训练模型，其他使用确定性预测"""
        predictions = []
        
        # 排列5使用真实训练的兼容版PLWSequenceLSTM模型
        if lottery_type == "plw":
            return self._get_plw_enhanced_lstm_predictions(num_predictions)
        elif lottery_type == "fc3d":
            return self._get_3d_enhanced_lstm_predictions(num_predictions)
        # 对于快乐8，使用专用的预测逻辑
        elif lottery_type == "kl8":
            return self._get_kl8_enhanced_lstm_predictions(num_predictions)
        
        # 大乐透和双色球使用序列LSTM增强模型
        else:
            return self._get_lottery_enhanced_lstm_predictions(lottery_type, num_predictions)

    def _get_plw_enhanced_lstm_predictions(self, num_predictions):
        """获取排列5增强LSTM预测结果"""
        predictions = []
        try:
            # 尝试加载排列5序列LSTM模型
            plw_model_path = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_sequence_lstm_model.pth')
            
            if os.path.exists(plw_model_path):
                # 导入兼容版排列5序列LSTM模型
                from algorithms.compatible_plw_lstm import load_compatible_plw_sequence_model
                from algorithms.plw_sequence_lstm import PLWDataProcessor
                
                # 加载训练好的模型，强制使用CPU以避免CUDA错误
                device = torch.device('cpu')  # 强制使用CPU
                plw_model = load_compatible_plw_sequence_model(plw_model_path, device)
                
                # 获取最近的历史数据
                plw_data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
                if os.path.exists(plw_data_file):
                    processor = PLWDataProcessor(plw_data_file, window_size=10)
                    recent_data = processor.get_recent_data()
                    
                    if recent_data is not None:
                        # 确保数据在正确的设备上
                        recent_data = recent_data.to(device)
                        
                        # 兼容版模型需要完整的10维特征（5个数字特征+5个区域转换特征）
                        input_data = recent_data.float()  # 确保输入是float类型
                        
                        # 确保数字特征在0-9范围内
                        input_data_clone = input_data.clone()
                        # 对数字特征（偶数索引位置）进行裁剪
                        for i in range(0, 10, 2):  # 0, 2, 4, 6, 8 位置是数字特征
                            input_data_clone[:, :, i] = torch.clamp(input_data_clone[:, :, i], 0, 9)
                        
                        # 使用真实模型预测
                        for i in range(num_predictions):
                            with torch.no_grad():
                                # 添加轻微噪声以产生多样性
                                noise_factor = 0.01 + i * 0.005
                                # 只对数字特征添加噪声
                                noisy_input = input_data_clone.clone()
                                for j in range(0, 10, 2):  # 只对数字特征添加噪声
                                    noise = torch.randn_like(noisy_input[:, :, j]) * noise_factor
                                    noisy_input[:, :, j] = noisy_input[:, :, j] + noise
                                
                                predictions_tensor, probabilities = plw_model.predict(noisy_input)
                                predicted_numbers = predictions_tensor[0].cpu().numpy().tolist()
                                
                                predictions.append({
                                    'red': predicted_numbers,
                                    'blue': [],  # 排列5没有蓝球
                                    'confidence': float(torch.max(probabilities[0]).item()) if probabilities is not None else 0.8
                                })
                        
                        self.log_emitter.new_log.emit(f"✅ 序列LSTM增强模式使用真实训练模型成功预测{num_predictions}组")
                        return predictions
                    else:
                        self.log_emitter.new_log.emit("⚠️ 无法获取排列5历史数据")
                else:
                    self.log_emitter.new_log.emit("⚠️ 排列5历史数据文件不存在")
            else:
                self.log_emitter.new_log.emit("⚠️ 排列5序列LSTM模型文件不存在")
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 排列5序列LSTM模型加载失败: {e}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误信息: {traceback.format_exc()}")
        
        # 模型不可用时，直接抛出异常而不是返回默认预测结果
        self.log_emitter.new_log.emit("❌ 序列LSTM增强模式模型不可用，无法生成预测")
        raise ValueError("序列LSTM增强模式模型不可用，无法生成预测")

    def _get_3d_enhanced_lstm_predictions(self, num_predictions):
        """获取3d增强LSTM预测结果"""
        predictions = []
        try:
            # 尝试加载3d序列LSTM模型
            fc3d_model_path = os.path.join(self.base_dir, 'scripts', 'fc3d', '3d_sequence_lstm_model.pth')
            
            if os.path.exists(fc3d_model_path):
                # 导入兼容版3d序列LSTM模型
                from algorithms.compatible_3d_lstm import load_compatible_3d_sequence_model
                from algorithms.fc3d_sequence_lstm import Data3DProcessor
                
                # 加载训练好的模型，强制使用CPU以避免CUDA错误
                device = torch.device('cpu')  # 强制使用CPU
                fc3d_model = load_compatible_3d_sequence_model(fc3d_model_path, device)
                
                # 获取最近的历史数据
                fc3d_data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
                if os.path.exists(fc3d_data_file):
                    processor = Data3DProcessor(fc3d_data_file, window_size=10)
                    recent_data = processor.get_recent_data()
                    
                    if recent_data is not None:
                        # 确保数据在正确的设备上
                        recent_data = recent_data.to(device)
                        
                        # 兼容版模型需要完整的10维特征（5个数字特征+5个区域转换特征）
                        input_data = recent_data.float()  # 确保输入是float类型
                        
                        # 确保数字特征在0-9范围内
                        input_data_clone = input_data.clone()
                        # 对数字特征（偶数索引位置）进行裁剪
                        for i in range(0, 10, 2):  # 0, 2, 4, 6, 8 位置是数字特征
                            input_data_clone[:, :, i] = torch.clamp(input_data_clone[:, :, i], 0, 9)
                        
                        # 使用真实模型预测
                        for i in range(num_predictions):
                            with torch.no_grad():
                                # 添加轻微噪声以产生多样性
                                noise_factor = 0.01 + i * 0.005
                                # 只对数字特征添加噪声
                                noisy_input = input_data_clone.clone()
                                for j in range(0, 10, 2):  # 只对数字特征添加噪声
                                    noise = torch.randn_like(noisy_input[:, :, j]) * noise_factor
                                    noisy_input[:, :, j] = noisy_input[:, :, j] + noise
                                
                                predictions_tensor, probabilities = fc3d_model.predict(noisy_input)
                                predicted_numbers = predictions_tensor[0].cpu().numpy().tolist()
                                
                                predictions.append({
                                    'red': predicted_numbers,
                                    'blue': [],  # 3d没有蓝球
                                    'confidence': float(torch.max(probabilities[0]).item()) if probabilities is not None else 0.8
                                })
                        
                        self.log_emitter.new_log.emit(f"✅ 序列LSTM增强模式使用真实训练模型成功预测{num_predictions}组")
                        return predictions
                    else:
                        self.log_emitter.new_log.emit("⚠️ 无法获取3d历史数据")
                else:
                    self.log_emitter.new_log.emit("⚠️ 3d历史数据文件不存在")
            else:
                self.log_emitter.new_log.emit("⚠️ 3d序列LSTM模型文件不存在")
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 3d序列LSTM模型加载失败: {e}")
            import traceback
            self.log_emitter.new_log.emit(f"详细错误信息: {traceback.format_exc()}")
        
        # 模型不可用时，直接抛出异常而不是返回默认预测结果
        self.log_emitter.new_log.emit("❌ 序列LSTM增强模式模型不可用，无法生成预测")
        raise ValueError("序列LSTM增强模式模型不可用，无法生成预测")

    def _get_kl8_enhanced_lstm_predictions(self, num_predictions):
        """获取快乐8增强LSTM预测结果"""
        predictions = []
        try:
            from algorithms.kl8_algorithms import KL8SequenceLSTM
            import pandas as pd
            
            # 加载快乐8历史数据
            data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
            if os.path.exists(data_file):
                df = pd.read_csv(data_file, encoding='utf-8')
                # 提取开奖号码（第3列到第22列，对应1号到20号）
                history_data = df.iloc[:, 2:22].values.tolist()
                
                # 创建并训练算法
                sl = KL8SequenceLSTM()
                sl.train(history_data)
                
                # 生成预测
                kl8_predictions = sl.predict(history_data, num_predictions)
                
                # 转换预测结果格式
                for pred in kl8_predictions:
                    if isinstance(pred, dict):
                        predictions.append({
                            'red': pred.get('red', []),
                            'blue': pred.get('blue', []),
                            'confidence': pred.get('confidence', 0.75),
                            'weights': pred.get('weights', {})
                        })
                return predictions
            else:
                print(f"⚠️ 快乐8历史数据文件不存在: {data_file}")
        except Exception as e:
            print(f"⚠️ 快乐8序列LSTM算法执行失败: {e}")
            import traceback
            traceback.print_exc()
            # 删除默认预测，直接抛出异常
            raise e

    def _get_lottery_enhanced_lstm_predictions(self, lottery_type, num_predictions):
        """获取大乐透和双色球增强LSTM预测结果"""
        predictions = []
        try:
            # 加载模型
            red_model, blue_model, scaler_X = self.load_models_with_cache(lottery_type)
            
            # 获取历史数据
            history_data = self.get_recent_lottery_data(lottery_type, window_size=10)
            
            if history_data is not None and len(history_data) >= 10:
                # 使用真实模型进行预测
                for i in range(num_predictions):
                    # 添加轻微噪声以产生多样性，但仍基于模型
                    noise_factor = 0.01 + i * 0.005  # 轻微噪声
                    red_pred, blue_pred = self.predict_with_trained_model(
                        red_model, blue_model, scaler_X, history_data, 
                        lottery_type, noise_factor
                    )
                    # 修复：确保即使模型预测失败也添加预测结果
                    if len(red_pred) > 0 and len(blue_pred) > 0:
                        predictions.append({
                            'red': red_pred,
                            'blue': blue_pred,
                            'confidence': 0.8 - (i * 0.05)  # 置信度逐渐降低
                        })
                
                if len(predictions) > 0:
                    self.log_emitter.new_log.emit(f"✅ 序列LSTM增强模式使用训练模型成功预测{len(predictions)}组")
                    return predictions
                else:
                    self.log_emitter.new_log.emit(f"⚠️ 序列LSTM增强模式未能生成有效预测")
            else:
                self.log_emitter.new_log.emit(f"⚠️ 历史数据不足")
        except Exception as model_error:
            self.log_emitter.new_log.emit(f"⚠️ 序列LSTM增强模式模型加载失败: {model_error}")
        
        # 模型不可用时，直接抛出异常而不是返回默认预测结果
        self.log_emitter.new_log.emit("❌ 序列LSTM增强模式模型不可用，无法生成预测")
        raise ValueError("序列LSTM增强模式模型不可用，无法生成预测")

    def get_gradient_boost_predictions(self, lottery_type, num_predictions):
        """获取梯度提升预测结果（奇偶平衡） - 改进版本，基于历史数据变化"""
        predictions = []
        
        # 对于快乐8，使用专用的KL8GradientBoosting算法
        if lottery_type == "kl8":
            try:
                from algorithms.kl8_algorithms import KL8GradientBoosting
                import pandas as pd
                
                # 加载快乐8历史数据
                data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
                if os.path.exists(data_file):
                    df = pd.read_csv(data_file, encoding='utf-8')
                    # 提取开奖号码（第3列到第22列，对应1号到20号）
                    history_data = df.iloc[:, 2:22].values.tolist()
                    
                    # 创建并训练算法
                    gb = KL8GradientBoosting()
                    gb.train(history_data)
                    
                    # 生成预测
                    kl8_predictions = gb.predict(history_data, num_predictions)
                    return kl8_predictions
                else:
                    print(f"⚠️ 快乐8历史数据文件不存在: {data_file}")
            except Exception as e:
                print(f"⚠️ 快乐8梯度提升算法执行失败: {e}")
                import traceback
                traceback.print_exc()
        
        # 使用确定性种子，但基于历史数据文件的修改时间来变化
        try:
            if lottery_type == "plw":
                data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
            elif lottery_type == "fc3d":
                data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
            elif lottery_type == "kl8":
                data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
            elif lottery_type == "ssq":
                data_file = os.path.join(self.base_dir, 'scripts', 'ssq', 'ssq_history.csv')
            else:  # dlt
                data_file = os.path.join(self.base_dir, 'scripts', 'dlt', 'dlt_history.csv')
                
            if os.path.exists(data_file):
                # 基于文件修改时间生成变化的种子
                file_mtime = os.path.getmtime(data_file)
                base_seed = int(file_mtime) % 1000000
            else:
                # 如果数据文件不存在，直接抛出异常而不是使用默认种子
                raise FileNotFoundError(f"历史数据文件不存在: {data_file}")
        except Exception as file_error:
            # 如果无法访问文件，直接抛出异常
            raise file_error
        
        for i in range(num_predictions):
            np.random.seed(base_seed + i * 73)
            import random
            random.seed(base_seed + i * 73)
            
            if lottery_type == "dlt":
                # 奇偶平衡特征
                odds = random.sample(range(1, 36, 2), 3)  # 奇数
                evens = random.sample(range(2, 36, 2), 2)  # 偶数
                red_numbers = sorted(odds + evens)
                blue_numbers = sorted(random.sample(range(1, 13), 2))
            elif lottery_type == "plw":
                # 排列5的奇偶平衡策略：0-9范围
                # 添加区域转换特征分析
                try:
                    from algorithms.plw_zone_features import PLWZoneFeatureExtractor, load_plw_history_data
                    plw_data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
                    if os.path.exists(plw_data_file):
                        # 加载历史数据并分析区域转换模式
                        zone_extractor = PLWZoneFeatureExtractor()
                        history_data = load_plw_history_data(plw_data_file)
                        if len(history_data) > 0:
                            zone_stats = zone_extractor.analyze_zone_transitions(history_data)
                            # 根据区域转换统计调整预测策略
                            total_transitions = sum(zone_stats.values())
                            if total_transitions > 0:
                                small_to_large_ratio = zone_stats['small_to_large'] / total_transitions
                                large_to_small_ratio = zone_stats['large_to_small'] / total_transitions
                                
                                # 根据历史转换模式调整数字选择
                                if small_to_large_ratio > large_to_small_ratio:
                                    # 倾向于选择大数区数字(5-9)
                                    preferred_numbers = [5, 6, 7, 8, 9]
                                    other_numbers = [0, 1, 2, 3, 4]
                                else:
                                    # 倾向于选择小数区数字(0-4)
                                    preferred_numbers = [0, 1, 2, 3, 4]
                                    other_numbers = [5, 6, 7, 8, 9]
                            else:
                                preferred_numbers = [0, 1, 2, 3, 4]
                                other_numbers = [5, 6, 7, 8, 9]
                        else:
                            preferred_numbers = [0, 1, 2, 3, 4]
                            other_numbers = [5, 6, 7, 8, 9]
                    else:
                        # 如果数据文件不存在，直接抛出异常而不是使用默认值
                        raise FileNotFoundError(f"排列5历史数据文件不存在: {plw_data_file}")
                except Exception as plw_error:
                    # 如果分析失败，直接抛出异常
                    raise plw_error
                
                # 奇偶平衡策略结合区域转换特征
                odds = random.sample([1, 3, 5, 7, 9], min(3, 5))  # 奇数
                evens = random.sample([0, 2, 4, 6, 8], min(2, 5 - len(odds)))  # 偶数
                
                # 如果数量不足，补充随机数字（优先选择preferred_numbers）
                red_numbers = odds + evens
                while len(red_numbers) < 5:
                    if random.random() < 0.7 and len(preferred_numbers) > 0:  # 70%概率选择preferred_numbers
                        candidate = random.choice(preferred_numbers)
                    else:
                        candidate = random.randint(0, 9)
                    red_numbers.append(candidate)
                
                # 排列5不需要排序，保持原始位置顺序
                red_numbers = red_numbers[:5]
                blue_numbers = []  # 排列5没有蓝球
            elif lottery_type == "fc3d":
                # 3D彩票的奇偶平衡策略：0-9范围，3个数字
                # 添加区域转换特征分析
                try:
                    from algorithms.fc3d_zone_features import Data3DZoneFeatureExtractor, load_3d_history_data
                    fc3d_data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
                    if os.path.exists(fc3d_data_file):
                        # 加载历史数据并分析区域转换模式
                        zone_extractor = Data3DZoneFeatureExtractor()
                        history_data = load_3d_history_data(fc3d_data_file)
                        if len(history_data) > 0:
                            zone_stats = zone_extractor.analyze_zone_transitions(history_data)
                            # 根据区域转换统计调整预测策略
                            total_transitions = sum(zone_stats.values())
                            if total_transitions > 0:
                                small_to_large_ratio = zone_stats['small_to_large'] / total_transitions
                                large_to_small_ratio = zone_stats['large_to_small'] / total_transitions
                                
                                # 根据历史转换模式调整数字选择
                                if small_to_large_ratio > large_to_small_ratio:
                                    # 倾向于选择大数区数字(5-9)
                                    preferred_numbers = [5, 6, 7, 8, 9]
                                    other_numbers = [0, 1, 2, 3, 4]
                                else:
                                    # 倾向于选择小数区数字(0-4)
                                    preferred_numbers = [0, 1, 2, 3, 4]
                                    other_numbers = [5, 6, 7, 8, 9]
                            else:
                                preferred_numbers = [0, 1, 2, 3, 4]
                                other_numbers = [5, 6, 7, 8, 9]
                        else:
                            preferred_numbers = [0, 1, 2, 3, 4]
                            other_numbers = [5, 6, 7, 8, 9]
                    else:
                        # 如果数据文件不存在，直接抛出异常而不是使用默认值
                        raise FileNotFoundError(f"3D彩票历史数据文件不存在: {fc3d_data_file}")
                except Exception as fc3d_error:
                    # 如果分析失败，直接抛出异常
                    raise fc3d_error
                
                # 奇偶平衡策略结合区域转换特征
                odds = random.sample([1, 3, 5, 7, 9], min(2, 5))  # 奇数
                evens = random.sample([0, 2, 4, 6, 8], min(1, 3 - len(odds)))  # 偶数
                
                # 如果数量不足，补充随机数字（优先选择preferred_numbers）
                red_numbers = odds + evens
                while len(red_numbers) < 3:
                    if random.random() < 0.7 and len(preferred_numbers) > 0:  # 70%概率选择preferred_numbers
                        candidate = random.choice(preferred_numbers)
                    else:
                        candidate = random.randint(0, 9)
                    red_numbers.append(candidate)
                
                # 3D彩票不需要排序，保持原始位置顺序
                red_numbers = red_numbers[:3]
                blue_numbers = []  # 3D彩票没有蓝球
            elif lottery_type == "kl8":
                # 快乐8：从1-80中选择10个不重复数字
                # 使用分层抽样策略确保覆盖不同区间
                zones = [
                    list(range(1, 17)),    # 1-16
                    list(range(17, 33)),   # 17-32
                    list(range(33, 49)),   # 33-48
                    list(range(49, 65)),   # 49-64
                    list(range(65, 81))    # 65-80
                ]
                
                red_numbers = []
                # 从每个区间选择1-3个数字
                for zone in zones:
                    count = random.randint(1, 3)
                    selected = random.sample(zone, min(count, len(zone)))
                    red_numbers.extend(selected)
                
                # 如果数量不足10个，随机补充
                while len(red_numbers) < 10:
                    candidate = random.randint(1, 80)
                    if candidate not in red_numbers:
                        red_numbers.append(candidate)
                
                # 如果数量超过10个，随机减少
                if len(red_numbers) > 10:
                    red_numbers = random.sample(red_numbers, 10)
                
                red_numbers = sorted(red_numbers)
                blue_numbers = []  # 快乐8没有蓝球
            else:  # ssq
                odds = random.sample(range(1, 34, 2), 3)
                evens = random.sample(range(2, 34, 2), 3)
                red_numbers = sorted(odds + evens)
                blue_numbers = [random.randint(1, 16)]

            
            predictions.append({
                'red': red_numbers,
                'blue': blue_numbers
            })
        
        return predictions

    def get_memory_network_predictions(self, lottery_type, num_predictions):
        """获取模式记忆网络预测结果（基于历史数据模式分析）"""
        predictions = []
        
        # 对于快乐8，使用专用的KL8MemoryNetwork算法
        if lottery_type == "kl8":
            try:
                from algorithms.kl8_algorithms import KL8MemoryNetwork
                import pandas as pd
                
                # 加载快乐8历史数据
                data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
                if os.path.exists(data_file):
                    df = pd.read_csv(data_file, encoding='utf-8')
                    # 提取开奖号码（第3列到第22列，对应1号到20号）
                    history_data = df.iloc[:, 2:22].values.tolist()
                    
                    # 创建并训练算法
                    mn = KL8MemoryNetwork()
                    mn.train(history_data)
                    
                    # 生成预测
                    kl8_predictions = mn.predict(history_data, num_predictions)
                    return kl8_predictions
                else:
                    print(f"⚠️ 快乐8历史数据文件不存在: {data_file}")
            except Exception as e:
                print(f"⚠️ 快乐8模式记忆网络算法执行失败: {e}")
                import traceback
                traceback.print_exc()
        
        # 多样化模式，避免过度连续
        memory_patterns = [
            "mixed_distribution",    # 混合分布
            "fibonacci_inspired",    # 斐波那契启发
            "prime_numbers",         # 质数偏好
            "zone_balanced",         # 分区平衡
            "historical_hot"         # 历史热门
        ]
        
        # 分析历史数据模式
        pattern_frequencies = {}  # 模式频率
        number_correlations = {}  # 数字关联性
        
        try:
            if lottery_type == "plw":
                data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
            elif lottery_type == "fc3d":
                data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
            elif lottery_type == "kl8":
                data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
            elif lottery_type == "ssq":
                data_file = os.path.join(self.base_dir, 'scripts', 'ssq', 'ssq_history.csv')
            else:  # dlt
                data_file = os.path.join(self.base_dir, 'scripts', 'dlt', 'dlt_history.csv')
                
            if os.path.exists(data_file):
                # 加载历史数据
                if lottery_type == "plw":
                    from algorithms.plw_zone_features import PLWZoneFeatureExtractor, load_plw_history_data
                    zone_extractor = PLWZoneFeatureExtractor()
                    history_data = load_plw_history_data(data_file)
                elif lottery_type == "fc3d":
                    from algorithms.fc3d_zone_features import Data3DZoneFeatureExtractor, load_3d_history_data
                    zone_extractor = Data3DZoneFeatureExtractor()
                    history_data = load_3d_history_data(data_file)
                elif lottery_type == "kl8":
                    import pandas as pd
                    df = pd.read_csv(data_file, encoding='utf-8')
                    # 提取开奖号码（第3列到第22列，对应1号到20号）
                    history_data = df.iloc[:, 2:22].values.tolist()
                else:
                    # 对于大乐透和双色球，使用通用的数据加载方式
                    import pandas as pd
                    df = pd.read_csv(data_file)
                    if lottery_type == "dlt":
                        # 大乐透：前5个红球
                        history_data = df.iloc[:, 1:6].values.tolist()
                    else:  # ssq
                        # 双色球：前6个红球
                        history_data = df.iloc[:, 1:7].values.tolist()
                
                if len(history_data) > 20:  # 需要足够的数据进行分析
                    # 分析数字关联性（连续出现的数字）
                    if lottery_type == "plw":
                        num_positions = 5
                    elif lottery_type == "fc3d":
                        num_positions = 3
                    elif lottery_type == "kl8":
                        num_positions = 20
                    elif lottery_type == "ssq":
                        num_positions = 6
                    else:  # dlt
                        num_positions = 5
                        
                    for i in range(1, len(history_data)):
                        prev_draw = history_data[i-1]
                        curr_draw = history_data[i]
                        for pos in range(min(num_positions, len(prev_draw), len(curr_draw))):
                            prev_num = prev_draw[pos]
                            curr_num = curr_draw[pos]
                            key = (prev_num, curr_num)
                            number_correlations[key] = number_correlations.get(key, 0) + 1
                    
                    # 分析模式频率（基于最近的数据）
                    recent_data = history_data[-20:]  # 分析最近20期
                    for draw in recent_data:
                        # 简单模式识别
                        if lottery_type == "plw":
                            if all(num <= 4 for num in draw):
                                pattern_frequencies["small_zone"] = pattern_frequencies.get("small_zone", 0) + 1
                            elif all(num >= 5 for num in draw):
                                pattern_frequencies["large_zone"] = pattern_frequencies.get("large_zone", 0) + 1
                            elif sum(1 for num in draw if num % 2 == 1) >= 3:
                                pattern_frequencies["odd_heavy"] = pattern_frequencies.get("odd_heavy", 0) + 1
                            elif sum(1 for num in draw if num % 2 == 0) >= 3:
                                pattern_frequencies["even_heavy"] = pattern_frequencies.get("even_heavy", 0) + 1
                        elif lottery_type == "fc3d":
                            if all(num <= 4 for num in draw):
                                pattern_frequencies["small_zone"] = pattern_frequencies.get("small_zone", 0) + 1
                            elif all(num >= 5 for num in draw):
                                pattern_frequencies["large_zone"] = pattern_frequencies.get("large_zone", 0) + 1
                            elif sum(1 for num in draw if num % 2 == 1) >= 2:
                                pattern_frequencies["odd_heavy"] = pattern_frequencies.get("odd_heavy", 0) + 1
                            elif sum(1 for num in draw if num % 2 == 0) >= 2:
                                pattern_frequencies["even_heavy"] = pattern_frequencies.get("even_heavy", 0) + 1
                        elif lottery_type == "kl8":
                            # 快乐8模式识别
                            small_count = sum(1 for num in draw if 1 <= num <= 20)
                            medium_count = sum(1 for num in draw if 21 <= num <= 40)
                            large_count = sum(1 for num in draw if 41 <= num <= 60)
                            extra_count = sum(1 for num in draw if 61 <= num <= 80)
                            
                            if small_count >= 4:
                                pattern_frequencies["small_zone"] = pattern_frequencies.get("small_zone", 0) + 1
                            elif large_count >= 4:
                                pattern_frequencies["large_zone"] = pattern_frequencies.get("large_zone", 0) + 1
                            elif medium_count >= 4:
                                pattern_frequencies["medium_zone"] = pattern_frequencies.get("medium_zone", 0) + 1
                            elif extra_count >= 4:
                                pattern_frequencies["extra_zone"] = pattern_frequencies.get("extra_zone", 0) + 1
                        else:
                            # 大乐透和双色球使用类似逻辑
                            small_count = sum(1 for num in draw if 1 <= num <= 11)
                            medium_count = sum(1 for num in draw if 12 <= num <= 22)
                            large_count = sum(1 for num in draw if 23 <= num <= 33)
                            
                            if small_count >= 3:
                                pattern_frequencies["small_zone"] = pattern_frequencies.get("small_zone", 0) + 1
                            elif large_count >= 3:
                                pattern_frequencies["large_zone"] = pattern_frequencies.get("large_zone", 0) + 1
                            elif medium_count >= 3:
                                pattern_frequencies["medium_zone"] = pattern_frequencies.get("medium_zone", 0) + 1
                else:
                    # 如果历史数据不足，直接抛出异常而不是使用默认值
                    raise ValueError("历史数据不足，无法计算权重")
            else:
                # 如果数据文件不存在，直接抛出异常而不是使用默认值
                raise FileNotFoundError(f"历史数据文件不存在: {data_file}")
        except Exception as analysis_error:
            # 如果分析失败，直接抛出异常而不是使用默认值
            raise analysis_error
        
        # 基于分析结果生成预测
        for i in range(num_predictions):
            import random
            
            # 设置基于时间变化的种子，增加随机性
            current_time_seed = int(time.time() * 1000) % 1000000
            random.seed(current_time_seed + i * 100)
            
            # 根据模式频率选择模式
            if pattern_frequencies:
                # 按频率选择模式
                total_freq = sum(pattern_frequencies.values())
                rand_val = random.random() * total_freq
                cumulative = 0
                selected_pattern = "mixed_distribution"
                
                for pattern, freq in pattern_frequencies.items():
                    cumulative += freq
                    if rand_val <= cumulative:
                        if pattern == "small_zone":
                            selected_pattern = "zone_balanced"  # 使用分区平衡模式
                        elif pattern == "large_zone":
                            selected_pattern = "zone_balanced"
                        elif pattern == "odd_heavy":
                            selected_pattern = "prime_numbers"  # 使用质数偏好模式
                        elif pattern == "even_heavy":
                            selected_pattern = "fibonacci_inspired"  # 使用斐波那契启发模式
                        break
            else:
                # 如果没有足够的模式数据，随机选择
                selected_pattern = memory_patterns[i % len(memory_patterns)]
            
            if lottery_type == "plw":
                # 排列5特殊处理：0-9范围，5个数字
                if selected_pattern == "mixed_distribution":
                    # 混合分布：从不同区间选择
                    low_zone = random.sample([0, 1, 2], 2)      # 低区 0-2
                    mid_zone = random.sample([3, 4, 5, 6], 2)   # 中区 3-6
                    high_zone = random.sample([7, 8, 9], 1)     # 高区 7-9
                    red_numbers = low_zone + mid_zone + high_zone
                elif selected_pattern == "fibonacci_inspired":
                    # 斐波那契启发：0-9范围内的数列
                    fib_like = [0, 1, 1, 2, 3, 5, 8]  # 0-9范围内的斐波那契数
                    base_nums = random.sample(fib_like, 3)
                    extra_nums = random.sample([n for n in range(10) if n not in base_nums], 2)
                    red_numbers = base_nums + extra_nums
                elif selected_pattern == "prime_numbers":
                    # 质数偏好：0-9范围内的质数
                    primes = [2, 3, 5, 7]  # 0-9范围内的质数
                    prime_nums = random.sample(primes, 2)
                    non_prime_nums = random.sample([0, 1, 4, 6, 8, 9], 3)  # 非质数
                    red_numbers = prime_nums + non_prime_nums
                elif selected_pattern == "zone_balanced":
                    # 分区平衡：确保各区间都有代表
                    zones = [[0, 1], [2, 3, 4], [5, 6], [7, 8, 9]]
                    red_numbers = []
                    for zone in zones[:4]:  # 只选择前4区
                        red_numbers.append(random.choice(zone))
                    red_numbers.append(random.randint(0, 9))  # 第5个数字
                else:  # historical_hot或其他模式
                    # 基于数字关联性预测
                    if number_correlations:
                        # 随机选择一个位置作为起点，根据关联性预测下一个数字
                        red_numbers = []
                        start_num = random.randint(0, 9)
                        red_numbers.append(start_num)
                        
                        # 根据关联性预测后续数字
                        for pos in range(1, 5):
                            # 找到以前一个数字为起点的关联性
                            candidates = [(next_num, freq) for (prev_num, next_num), freq in number_correlations.items() if prev_num == red_numbers[-1]]
                            if candidates:
                                # 按频率选择
                                total_freq = sum(freq for _, freq in candidates)
                                if total_freq > 0:
                                    rand_val = random.random() * total_freq
                                    cumulative = 0
                                    for next_num, freq in candidates:
                                        cumulative += freq
                                        if rand_val <= cumulative:
                                            red_numbers.append(next_num)
                                            break
                                else:
                                    # 如果频率为0，直接抛出异常而不是使用默认值
                                    raise ValueError("数字关联性频率为0")
                            else:
                                # 如果没有候选数字，直接抛出异常而不是使用默认值
                                raise ValueError("没有找到候选数字")
                    else:
                        # 热门数字（0-9范围）
                        hot_numbers = [1, 2, 5, 7, 8]
                        selected_hot = random.sample(hot_numbers, 3)
                        cold_numbers = [0, 3, 4, 6, 9]
                        selected_cold = random.sample(cold_numbers, 2)
                        red_numbers = selected_hot + selected_cold
                
                # 排列5不需要排序，保持位置顺序
                red_numbers = red_numbers[:5]
                blue_numbers = []  # 排列5没有蓝球
            elif lottery_type == "fc3d":
                # 3D彩票特殊处理：0-9范围，3个数字
                if selected_pattern == "mixed_distribution":
                    # 混合分布：从不同区间选择
                    low_zone = random.sample([0, 1, 2], 1)      # 低区 0-2
                    mid_zone = random.sample([3, 4, 5, 6], 1)   # 中区 3-6
                    high_zone = random.sample([7, 8, 9], 1)     # 高区 7-9
                    red_numbers = low_zone + mid_zone + high_zone
                elif selected_pattern == "fibonacci_inspired":
                    # 斐波那契启发：0-9范围内的数列
                    fib_like = [0, 1, 1, 2, 3, 5, 8]  # 0-9范围内的斐波那契数
                    base_nums = random.sample(fib_like, 2)
                    extra_nums = random.sample([n for n in range(10) if n not in base_nums], 1)
                    red_numbers = base_nums + extra_nums
                elif selected_pattern == "prime_numbers":
                    # 质数偏好：0-9范围内的质数
                    primes = [2, 3, 5, 7]  # 0-9范围内的质数
                    prime_nums = random.sample(primes, 1)
                    non_prime_nums = random.sample([0, 1, 4, 6, 8, 9], 2)  # 非质数
                    red_numbers = prime_nums + non_prime_nums
                elif selected_pattern == "zone_balanced":
                    # 分区平衡：确保各区间都有代表
                    zones = [[0, 1], [2, 3, 4], [5, 6], [7, 8, 9]]
                    red_numbers = []
                    for zone in zones[:3]:  # 只选择前3区
                        red_numbers.append(random.choice(zone))
                else:  # historical_hot或其他模式
                    # 基于数字关联性预测
                    if number_correlations:
                        # 随机选择一个位置作为起点，根据关联性预测下一个数字
                        red_numbers = []
                        start_num = random.randint(0, 9)
                        red_numbers.append(start_num)
                        
                        # 根据关联性预测后续数字
                        for pos in range(1, 3):
                            # 找到以前一个数字为起点的关联性
                            candidates = [(next_num, freq) for (prev_num, next_num), freq in number_correlations.items() if prev_num == red_numbers[-1]]
                            if candidates:
                                # 按频率选择
                                total_freq = sum(freq for _, freq in candidates)
                                if total_freq > 0:
                                    rand_val = random.random() * total_freq
                                    cumulative = 0
                                    for next_num, freq in candidates:
                                        cumulative += freq
                                        if rand_val <= cumulative:
                                            red_numbers.append(next_num)
                                            break
                                else:
                                    # 如果频率为0，直接抛出异常而不是使用默认值
                                    raise ValueError("数字关联性频率为0")
                            else:
                                # 如果没有候选数字，直接抛出异常而不是使用默认值
                                raise ValueError("没有找到候选数字")
                    else:
                        # 热门数字（0-9范围）
                        hot_numbers = [1, 2, 5, 7, 8]
                        selected_hot = random.sample(hot_numbers, 1)
                        cold_numbers = [0, 3, 4, 6, 9]
                        selected_cold = random.sample(cold_numbers, 2)
                        red_numbers = selected_hot + selected_cold
                
                # 3D彩票不需要排序，保持位置顺序
                red_numbers = red_numbers[:3]
                blue_numbers = []  # 3D彩票没有蓝球

            elif lottery_type == "dlt":
                # 大乐透保持原有逻辑
                if selected_pattern == "mixed_distribution":
                    # 混合分布：从不同区间选择
                    low_zone = random.sample(range(1, 12), 2)     # 低区 1-11
                    mid_zone = random.sample(range(12, 24), 2)    # 中区 12-23
                    high_zone = random.sample(range(24, 36), 1)   # 高区 24-35
                    red_numbers = sorted(low_zone + mid_zone + high_zone)
                elif selected_pattern == "fibonacci_inspired":
                    # 斐波那奇数列启发：但不完全按斐波那契
                    fib_like = [1, 2, 3, 5, 8, 13, 21, 34]
                    base_nums = random.sample(fib_like, 3)
                    # 添加随机数字避免过度规律
                    extra_nums = random.sample([n for n in range(1, 36) if n not in base_nums], 2)
                    red_numbers = sorted(base_nums + extra_nums)
                elif selected_pattern == "prime_numbers":
                    # 质数偏好：优先选择质数但不全部是质数
                    primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
                    prime_nums = random.sample(primes, 3)
                    non_prime_nums = random.sample([n for n in range(1, 36) if n not in primes], 2)
                    red_numbers = sorted(prime_nums + non_prime_nums)
                elif selected_pattern == "zone_balanced":
                    # 分区平衡：确保各区间都有代表
                    zones = [
                        range(1, 8),    # 第一区
                        range(8, 15),   # 第二区
                        range(15, 22),  # 第三区
                        range(22, 29),  # 第四区
                        range(29, 36)   # 第五区
                    ]
                    red_numbers = []
                    for zone in zones:
                        red_numbers.append(random.choice(list(zone)))
                    red_numbers = sorted(red_numbers)
                else:  # historical_hot
                    # 模拟历史热门号码（根据实际统计数据设定）
                    hot_numbers = [7, 14, 21, 28, 35, 9, 16, 23, 30, 12, 19, 26, 33]
                    selected_hot = random.sample(hot_numbers, 3)
                    cold_numbers = [1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 17, 18, 20, 22, 24, 25, 27, 29, 31, 32, 34]
                    selected_cold = random.sample(cold_numbers, 2)
                    red_numbers = sorted(selected_hot + selected_cold)
                
                # 蓝球选择保持原有逻辑
                blue_numbers = sorted(random.sample(range(1, 13), 2))
            else:  # ssq
                # 双色球保持原有逻辑
                if selected_pattern == "mixed_distribution":
                    low_zone = random.sample(range(1, 12), 2)
                    mid_zone = random.sample(range(12, 23), 2)
                    high_zone = random.sample(range(23, 34), 2)
                    red_numbers = sorted(low_zone + mid_zone + high_zone)
                elif selected_pattern == "fibonacci_inspired":
                    fib_like = [1, 2, 3, 5, 8, 13, 21, 34]
                    base_nums = random.sample(fib_like, 3)
                    extra_nums = random.sample([n for n in range(1, 34) if n not in base_nums], 3)
                    red_numbers = sorted(base_nums + extra_nums)
                elif selected_pattern == "prime_numbers":
                    primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
                    prime_nums = random.sample(primes, 3)
                    non_prime_nums = random.sample([n for n in range(1, 34) if n not in primes], 3)
                    red_numbers = sorted(prime_nums + non_prime_nums)
                elif selected_pattern == "zone_balanced":
                    zones = [
                        range(1, 7),    # 第一区
                        range(7, 13),   # 第二区
                        range(13, 19),  # 第三区
                        range(19, 25),  # 第四区
                        range(25, 34)   # 第五区
                    ]
                    red_numbers = []
                    for zone in zones:
                        red_numbers.append(random.choice(list(zone)))
                    red_numbers = sorted(red_numbers)
                else:  # historical_hot
                    hot_numbers = [1, 6, 7, 9, 11, 12, 16, 19, 21, 23, 25, 27, 29, 31, 32, 33]
                    selected_hot = random.sample(hot_numbers, 3)
                    cold_numbers = [2, 3, 4, 5, 8, 10, 13, 14, 15, 17, 18, 20, 22, 24, 26, 28, 30]
                    selected_cold = random.sample(cold_numbers, 3)
                    red_numbers = sorted(selected_hot + selected_cold)
                
                # 蓝球选择保持原有逻辑
                blue_numbers = [random.randint(1, 16)]
            
            predictions.append({
                'red': red_numbers,
                'blue': blue_numbers
            })
        
        return predictions

    def get_weighted_expert_predictions(self, lottery_type, num_predictions):
        """获取权重维度专家系统预测结果（基于历史数据分析）"""
        predictions = []
        
        # 对于快乐8，使用专用的KL8WeightedExpert算法
        if lottery_type == "kl8":
            try:
                from algorithms.kl8_algorithms import KL8WeightedExpert
                import pandas as pd
                
                # 加载快乐8历史数据
                data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
                if os.path.exists(data_file):
                    df = pd.read_csv(data_file, encoding='utf-8')
                    # 提取开奖号码（第3列到第22列，对应1号到20号）
                    history_data = df.iloc[:, 2:22].values.tolist()
                    
                    # 创建并训练算法
                    we = KL8WeightedExpert()
                    we.train(history_data)
                    
                    # 生成预测
                    kl8_predictions = we.predict(history_data, num_predictions)
                    return kl8_predictions
                else:
                    print(f"⚠️ 快乐8历史数据文件不存在: {data_file}")
            except Exception as e:
                print(f"⚠️ 快乐8权重专家系统算法执行失败: {e}")
                import traceback
                traceback.print_exc()
                # 删除默认预测，直接抛出异常
                raise e
        
        # 分析历史数据以计算权重
        number_weights = {}  # 数字权重
        position_weights = {}  # 位置权重
        consecutive_patterns = {}  # 连号模式
        history_data = []  # 初始化历史数据变量
        
        try:
            if lottery_type == "plw":
                data_file = os.path.join(self.base_dir, 'scripts', 'plw', 'plw_history.csv')
            elif lottery_type == "fc3d":
                data_file = os.path.join(self.base_dir, 'scripts', 'fc3d', 'fc3d_history.csv')
            elif lottery_type == "kl8":
                data_file = os.path.join(self.base_dir, 'scripts', 'kl8', 'kl8_history.csv')
            elif lottery_type == "ssq":
                data_file = os.path.join(self.base_dir, 'scripts', 'ssq', 'ssq_history.csv')
            else:  # dlt
                data_file = os.path.join(self.base_dir, 'scripts', 'dlt', 'dlt_history.csv')
                
            if os.path.exists(data_file):
                # 加载历史数据
                if lottery_type == "plw":
                    from algorithms.plw_zone_features import PLWZoneFeatureExtractor, load_plw_history_data
                    zone_extractor = PLWZoneFeatureExtractor()
                    history_data = load_plw_history_data(data_file)
                elif lottery_type == "fc3d":
                    from algorithms.fc3d_zone_features import Data3DZoneFeatureExtractor, load_3d_history_data
                    zone_extractor = Data3DZoneFeatureExtractor()
                    history_data = load_3d_history_data(data_file)
                elif lottery_type == "kl8":
                    import pandas as pd
                    df = pd.read_csv(data_file, encoding='utf-8')
                    # 提取开奖号码（第3列到第22列，对应1号到20号）
                    history_data = df.iloc[:, 2:22].values.tolist()
                else:
                    # 对于大乐透和双色球，使用通用的数据加载方式
                    import pandas as pd
                    df = pd.read_csv(data_file)
                    if lottery_type == "dlt":
                        # 大乐透：前5个红球
                        history_data = df.iloc[:, 1:6].values.tolist()
                    else:  # ssq
                        # 双色球：前6个红球
                        history_data = df.iloc[:, 1:7].values.tolist()
                
                if len(history_data) > 10:  # 需要足够的数据进行分析
                    # 计算每个数字的出现频率权重
                    number_frequency = {}
                    for draw in history_data:
                        for num in draw:
                            number_frequency[num] = number_frequency.get(num, 0) + 1
                    
                    # 转换为权重（热门数字权重高，冷门数字权重低）
                    total_draws = len(history_data)
                    
                    if lottery_type == "plw":
                        # 排列5：0-9范围
                        for num in range(10):
                            freq = number_frequency.get(num, 0)
                            # 基础权重 + 频率权重（热门数字权重更高）
                            number_weights[num] = 1.0 + (freq / total_draws) * 2.0
                        
                        # 计算位置权重（每个位置上各数字的出现频率）
                        position_frequency = {}
                        for draw in history_data:
                            for pos, num in enumerate(draw):
                                if pos not in position_frequency:
                                    position_frequency[pos] = {}
                                position_frequency[pos][num] = position_frequency[pos].get(num, 0) + 1
                        
                        # 转换为位置权重
                        for pos in range(5):
                            if pos not in position_weights:
                                position_weights[pos] = {}
                            pos_total = sum(position_frequency[pos].values())
                            for num in range(10):
                                freq = position_frequency[pos].get(num, 0)
                                position_weights[pos][num] = 1.0 + (freq / pos_total) * 1.5 if pos_total > 0 else 1.0
                        
                        # 分析连号模式
                        for i in range(1, len(history_data)):
                            prev_draw = history_data[i-1]
                            curr_draw = history_data[i]
                            for pos in range(5):
                                if prev_draw[pos] == curr_draw[pos]:
                                    consecutive_patterns[pos] = consecutive_patterns.get(pos, 0) + 1
                    elif lottery_type == "fc3d":
                        # 3D彩票：0-9范围
                        for num in range(10):
                            freq = number_frequency.get(num, 0)
                            # 基础权重 + 频率权重（热门数字权重更高）
                            number_weights[num] = 1.0 + (freq / total_draws) * 2.0
                        
                        # 计算位置权重（每个位置上各数字的出现频率）
                        position_frequency = {}
                        for draw in history_data:
                            for pos, num in enumerate(draw):
                                if pos not in position_frequency:
                                    position_frequency[pos] = {}
                                position_frequency[pos][num] = position_frequency[pos].get(num, 0) + 1
                        
                        # 转换为位置权重
                        for pos in range(3):
                            if pos not in position_weights:
                                position_weights[pos] = {}
                            pos_total = sum(position_frequency[pos].values())
                            for num in range(10):
                                freq = position_frequency[pos].get(num, 0)
                                position_weights[pos][num] = 1.0 + (freq / pos_total) * 1.5 if pos_total > 0 else 1.0
                        
                        # 分析连号模式
                        for i in range(1, len(history_data)):
                            prev_draw = history_data[i-1]
                            curr_draw = history_data[i]
                            for pos in range(3):
                                if prev_draw[pos] == curr_draw[pos]:
                                    consecutive_patterns[pos] = consecutive_patterns.get(pos, 0) + 1
                    elif lottery_type == "kl8":
                        # 快乐8：1-80范围
                        for num in range(1, 81):
                            freq = number_frequency.get(num, 0)
                            # 基础权重 + 频率权重（热门数字权重更高）
                            number_weights[num] = 1.0 + (freq / total_draws) * 1.5
                        
                        # 计算位置权重（每个位置上各数字的出现频率）
                        position_frequency = {}
                        for draw in history_data:
                            for pos, num in enumerate(draw):
                                if pos not in position_frequency:
                                    position_frequency[pos] = {}
                                position_frequency[pos][num] = position_frequency[pos].get(num, 0) + 1
                        
                        # 转换为位置权重
                        for pos in range(20):
                            if pos not in position_weights:
                                position_weights[pos] = {}
                            pos_total = sum(position_frequency[pos].values())
                            for num in range(1, 81):
                                freq = position_frequency[pos].get(num, 0)
                                position_weights[pos][num] = 1.0 + (freq / pos_total) * 1.2 if pos_total > 0 else 1.0
                        
                        # 分析连号模式
                        for i in range(1, len(history_data)):
                            prev_draw = history_data[i-1]
                            curr_draw = history_data[i]
                            for pos in range(20):
                                if prev_draw[pos] == curr_draw[pos]:
                                    consecutive_patterns[pos] = consecutive_patterns.get(pos, 0) + 1
                    else:
                        # 大乐透和双色球
                        max_val = 35 if lottery_type == "dlt" else 33
                        for num in range(1, max_val + 1):
                            freq = number_frequency.get(num, 0)
                            # 基础权重 + 频率权重（热门数字权重更高）
                            number_weights[num] = 1.0 + (freq / total_draws) * 1.5
                        
                        # 计算位置权重
                        target_count = 5 if lottery_type == "dlt" else 6
                        position_frequency = {}
                        for draw in history_data:
                            for pos, num in enumerate(draw):
                                if pos not in position_frequency:
                                    position_frequency[pos] = {}
                                position_frequency[pos][num] = position_frequency[pos].get(num, 0) + 1
                        
                        # 转换为位置权重
                        for pos in range(target_count):
                            if pos not in position_weights:
                                position_weights[pos] = {}
                            pos_total = sum(position_frequency[pos].values())
                            for num in range(1, max_val + 1):
                                freq = position_frequency[pos].get(num, 0)
                                position_weights[pos][num] = 1.0 + (freq / pos_total) * 1.2 if pos_total > 0 else 1.0
                        
                        # 分析连号模式
                        for i in range(1, len(history_data)):
                            prev_draw = history_data[i-1]
                            curr_draw = history_data[i]
                            for pos in range(target_count):
                                if prev_draw[pos] == curr_draw[pos]:
                                    consecutive_patterns[pos] = consecutive_patterns.get(pos, 0) + 1
                else:
                    # 如果历史数据不足，直接抛出异常而不是使用默认权重
                    raise ValueError("历史数据不足，无法计算权重")
            else:
                # 如果数据文件不存在，直接抛出异常而不是使用默认权重
                raise FileNotFoundError(f"历史数据文件不存在: {data_file}")
        except Exception as weight_error:
            # 如果分析失败，直接抛出异常而不是使用默认权重
            raise weight_error
        
        # 基于分析结果生成预测（去除随机性，产生确定性结果）
        for i in range(num_predictions):
            # ... existing code ...
            
            if lottery_type == "plw":
                max_val = 9
                target_count = 5
                start_range = 0
            elif lottery_type == "fc3d":
                max_val = 9
                target_count = 3
                start_range = 0
            elif lottery_type == "kl8":
                max_val = 80
                target_count = 20
                start_range = 1
            elif lottery_type == "dlt":
                max_val = 35
                target_count = 5
                start_range = 1
            else:  # ssq
                max_val = 33
                target_count = 6
                start_range = 1
            
            if lottery_type == "plw":
                # 排列5特殊处理
                red_numbers = []
                
                # 基于位置权重和数字权重生成预测
                for pos in range(5):
                    # 计算该位置上各数字的综合权重
                    weighted_scores = []
                    for num in range(10):
                        # 综合权重 = 数字权重 * 位置权重
                        number_weight = number_weights.get(num, 1.0)
                        pos_weight = position_weights.get(pos, {}).get(num, 1.0)
                        total_weight = number_weight * pos_weight
                        
                        # 考虑连号因素（仅当不是第一个位置时）
                        if pos > 0 and len(red_numbers) > 0:
                            # 检查是否与前一个位置相同
                            if consecutive_patterns.get(pos, 0) > len(history_data) * 0.3:  # 如果连号比例较高
                                if red_numbers[pos-1] == num:
                                    total_weight *= 1.5
                        
                        weighted_scores.append((num, total_weight))
                    
                    # 按权重排序
                    weighted_scores.sort(key=lambda x: x[1], reverse=True)
                    
                    # 选择权重最高的数字，但如果该数字已在之前位置选择过，则选择下一个权重最高的未选择数字
                    selected_num = None
                    for num, score in weighted_scores:
                        if num not in red_numbers:
                            selected_num = num
                            break
                    
                    # 如果所有数字都已在之前位置选择过，则选择权重最高的数字（允许重复）
                    if selected_num is None:
                        selected_num = weighted_scores[0][0]
                        
                    red_numbers.append(selected_num)
                
                # 确保结果在合理范围内
                red_numbers = [max(0, min(9, num)) for num in red_numbers]
                blue_numbers = []  # 排列5没有蓝球
            elif lottery_type == "fc3d":
                # 3D彩票特殊处理
                red_numbers = []
                
                # 基于位置权重和数字权重生成预测
                for pos in range(3):
                    # 计算该位置上各数字的综合权重
                    weighted_scores = []
                    for num in range(10):
                        # 综合权重 = 数字权重 * 位置权重
                        number_weight = number_weights.get(num, 1.0)
                        pos_weight = position_weights.get(pos, {}).get(num, 1.0)
                        total_weight = number_weight * pos_weight
                        
                        # 考虑连号因素（仅当不是第一个位置时）
                        if pos > 0 and len(red_numbers) > 0:
                            # 检查是否与前一个位置相同
                            if consecutive_patterns.get(pos, 0) > len(history_data) * 0.3:  # 如果连号比例较高
                                if red_numbers[pos-1] == num:
                                    total_weight *= 1.5
                        
                        weighted_scores.append((num, total_weight))
                    
                    # 按权重排序
                    weighted_scores.sort(key=lambda x: x[1], reverse=True)
                    
                    # 选择权重最高的数字，但如果该数字已在之前位置选择过，则选择下一个权重最高的未选择数字
                    selected_num = None
                    for num, score in weighted_scores:
                        if num not in red_numbers:
                            selected_num = num
                            break
                    
                    # 如果所有数字都已在之前位置选择过，则选择权重最高的数字（允许重复）
                    if selected_num is None:
                        selected_num = weighted_scores[0][0]
                        
                    red_numbers.append(selected_num)
                
                # 确保结果在合理范围内
                red_numbers = [max(0, min(9, num)) for num in red_numbers]
                blue_numbers = []  # 3D彩票没有蓝球

            else:
                # 其他彩票类型保持原有逻辑，但基于分析结果调整权重 
                consecutive_weights = {}
                for num in range(start_range, max_val + 1):
                    # 基于数字特性的权重计算
                    base_weight = 1.0
                    
                    # 数字位置权重：中间数字略高权重
                    position_factor = 1.0 + 0.3 * (1 - abs(num - max_val/2) / (max_val/2))
                    
                    # 尾数权重：某些尾数更受欢迎
                    last_digit = num % 10
                    digit_factor = 1.2 if last_digit in [1, 3, 7, 9] else 1.0
                    
                    # 周期性权重：模拟周期性热门
                    cycle_factor = 1.0 + 0.2 * abs((num + i * 7) % 13 - 6.5) / 6.5
                    
                    total_weight = base_weight * position_factor * digit_factor * cycle_factor
                    consecutive_weights[num] = total_weight
                
                # 斜连号权重计算（模拟右斜和左斜趋势）
                diagonal_bonus = {}
                for num in range(start_range, max_val + 1):
                    # 右斜权重：数字递增趋势
                    right_diagonal = 1.0 + 0.1 * ((num + i * 2) % 7) / 7.0
                    
                    # 左斜权重：数字递减趋势
                    left_diagonal = 1.0 + 0.1 * ((max_val - num + i * 3) % 5) / 5.0
                    
                    # 取较高权重
                    diagonal_bonus[num] = max(right_diagonal, left_diagonal)
                
                # 按权重选择红球，避免过度集中
                weighted_scores = []
                for num in range(start_range, max_val + 1):
                    total_weight = consecutive_weights.get(num, 1.0) * diagonal_bonus.get(num, 1.0)
            
                    # 修改: 使用确定性权重
                    final_score = total_weight
                    weighted_scores.append((num, final_score))
                
                # 按权重排序并选择前面的数字
                weighted_scores.sort(key=lambda x: x[1], reverse=True)
          
                # 修改: 直接选择权重最高的数字
                red_numbers = [x[0] for x in weighted_scores[:target_count]]
            
                # 修改: 统一对结果进行排序
                red_numbers = sorted(red_numbers[:target_count])
                
              
                # 修改: 蓝球选择使用确定性方法
                if lottery_type == "dlt":
                    # 大乐透蓝球：2个蓝球，范围1-12
                    blue_weights = {}
                    for num in range(1, 13):
                        # 模拟蓝球热门度
                        popularity = 1.0 + 0.3 * ((num + i * 5) % 7) / 7.0
                        blue_weights[num] = popularity
                    
                    # 按权重排序并选择前2个
                    blue_candidates = list(range(1, 13))
                    blue_candidates.sort(key=lambda x: blue_weights[x], reverse=True)
                    blue_numbers = sorted(blue_candidates[:2])
                else:
                    # 双色球蓝球：1个蓝球，范围1-16
                    blue_base = (i * 7 + 3) % 16 + 1
                    blue_numbers = [blue_base]
            
            predictions.append({
                'red': red_numbers,
                'blue': blue_numbers
            })
        
        return predictions

    def train_lstm_model(self, lottery_key):
        """训练LSTM-CRF模型"""
        try:
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"🎓 开始训练 {lottery_name} LSTM-CRF模型...")
            
            # 禁用训练按钮防止重复训练
            lstm_train_btn = self.findChild(QPushButton, f"lstm_train_btn_{lottery_key}")
            if lstm_train_btn:
                lstm_train_btn.setEnabled(False)
                lstm_train_btn.setText("🔄 LSTM训练中...")
            
            # 启动训练线程
            self.train_thread = TrainModelThread(lottery_key)
            self.train_thread.log_signal.connect(self.update_log)
            self.train_thread.finished_signal.connect(lambda: self.on_lstm_train_finished(lottery_key))
            self.train_thread.start()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ LSTM训练启动失败: {str(e)}")

    def train_sequence_lstm_model(self, lottery_key):
        """训练序列LSTM模型（支持排列5和3D）"""
        try:
            if lottery_key not in ["plw", "fc3d"]:
                self.log_emitter.new_log.emit("⚠️ 序列LSTM模型目前仅支持排列5和3D彩票")
                return
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"🔥 开始训练{lottery_name}序列LSTM模型...")
            
            # 禁用训练按钮
            seq_train_btn = self.findChild(QPushButton, f"seq_lstm_train_btn_{lottery_key}")
            if seq_train_btn:
                seq_train_btn.setEnabled(False)
                seq_train_btn.setText("🔄 序列LSTM训练中...")
            
            # 启动序列LSTM训练线程
            self.seq_train_thread = SequenceLSTMTrainThread(lottery_key)
            self.seq_train_thread.log_signal.connect(self.update_log)
            self.seq_train_thread.finished_signal.connect(lambda: self.on_sequence_train_finished(lottery_key))
            self.seq_train_thread.start()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 序列LSTM训练启动失败: {str(e)}")

    def train_incremental_model(self, lottery_key):
        """增量训练模型（仅排列5）"""
        try:
            if lottery_key != "plw":
                self.log_emitter.new_log.emit("⚠️ 增量训练目前仅支持排列5")
                return
            
            self.log_emitter.new_log.emit("🔄 开始排列5增量训练...")
            
            # 禁用训练按钮
            incremental_train_btn = self.findChild(QPushButton, f"incremental_train_btn_{lottery_key}")
            if incremental_train_btn:
                incremental_train_btn.setEnabled(False)
                incremental_train_btn.setText("🔄 增量训练中...")
            
            # 启动增量训练线程
            self.incremental_train_thread = IncrementalTrainThread(lottery_key)
            self.incremental_train_thread.log_signal.connect(self.update_log)
            self.incremental_train_thread.finished_signal.connect(lambda: self.on_incremental_train_finished(lottery_key))
            self.incremental_train_thread.start()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 增量训练启动失败: {str(e)}")

    def train_enhanced_lstm_crf_model(self, lottery_key):
        """训练增强版LSTM-CRF模型"""
        try:
            self.log_emitter.new_log.emit(f"💎 开始训练{name_path[lottery_key]['name']}增强版LSTM-CRF模型...")
            
            # 禁用训练按钮
            enhanced_crf_train_btn = self.findChild(QPushButton, f"enhanced_crf_train_btn_{lottery_key}")
            if enhanced_crf_train_btn:
                enhanced_crf_train_btn.setEnabled(False)
                enhanced_crf_train_btn.setText("🔄 增强版LSTM-CRF训练中...")
            
            # 启动增强版LSTM-CRF训练线程
            self.enhanced_crf_train_thread = EnhancedLSTMCRFTrainThread(lottery_key)
            self.enhanced_crf_train_thread.log_signal.connect(self.update_log)
            self.enhanced_crf_train_thread.finished_signal.connect(lambda: self.on_enhanced_crf_train_finished(lottery_key))
            self.enhanced_crf_train_thread.start()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 增强版LSTM-CRF训练启动失败: {str(e)}")

    def train_incremental_model(self, lottery_key):
        """增量训练模型"""
        try:
            self.log_emitter.new_log.emit(f"🔄 开始{name_path[lottery_key]['name']}增量训练...")
            
            # 禁用训练按钮
            incremental_train_btn = self.findChild(QPushButton, f"incremental_train_btn_{lottery_key}")
            if incremental_train_btn:
                incremental_train_btn.setEnabled(False)
                incremental_train_btn.setText("🔄 增量训练中...")
            
            # 启动增量训练线程
            self.incremental_train_thread = IncrementalTrainThread(lottery_key)
            self.incremental_train_thread.log_signal.connect(self.update_log)
            self.incremental_train_thread.finished_signal.connect(lambda: self.on_incremental_train_finished(lottery_key))
            self.incremental_train_thread.start()
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 增量训练启动失败: {str(e)}")
    def on_enhanced_crf_train_finished(self, lottery_key):
        """当增强版LSTM-CRF训练完成时的处理"""
        try:
            # 启用训练按钮
            enhanced_crf_train_btn = self.findChild(QPushButton, f"enhanced_crf_train_btn_{lottery_key}")
            if enhanced_crf_train_btn:
                enhanced_crf_train_btn.setEnabled(True)
                enhanced_crf_train_btn.setText("💎 训练 增强版LSTM-CRF 模型")
            
            # 更新模型状态
            self.update_model_status(lottery_key)
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"✅ {lottery_name} 增强版LSTM-CRF模型训练完成")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 增强版LSTM-CRF训练完成处理失败: {e}")                
                                
    def reload_specific_model(self, lottery_key):
        """重新加载指定彩票类型的模型"""
        try:
            # 清空指定类型的模型缓存
            self.model_cache[lottery_key] = {'models': None, 'timestamp': None}
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"🔄 正在重新加载 {lottery_name} 模型...")
            
            # 更新模型状态显示
            self.update_model_status(lottery_key)
            
            self.log_emitter.new_log.emit(f"✅ {lottery_name} 模型重新加载完成")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"❗ 重新加载模型失败: {str(e)}")

    def on_lstm_train_finished(self, lottery_key):
        """当LSTM训练完成时的处理"""
        try:
            # 启用训练按钮
            lstm_train_btn = self.findChild(QPushButton, f"lstm_train_btn_{lottery_key}")
            if lstm_train_btn:
                lstm_train_btn.setEnabled(True)
                lstm_train_btn.setText("🤖 训练 LSTM-CRF 模型")
            
            # 更新模型状态
            self.update_model_status(lottery_key)
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"✅ {lottery_name} LSTM-CRF模型训练完成")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ LSTM训练完成处理失败: {e}")

    def on_sequence_train_finished(self, lottery_key):
        """当序列LSTM训练完成时的处理"""
        try:
            # 启用训练按钮
            seq_train_btn = self.findChild(QPushButton, f"seq_lstm_train_btn_{lottery_key}")
            if seq_train_btn:
                seq_train_btn.setEnabled(True)
                seq_train_btn.setText("🔥 训练 序列LSTM 模型")
            
            # 更新模型状态
            self.update_model_status(lottery_key)
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"✅ {lottery_name} 序列LSTM模型训练完成")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 序列LSTM训练完成处理失败: {e}")

    def on_incremental_train_finished(self, lottery_key):
        """当增量训练完成时的处理"""
        try:
            # 启用训练按钮
            incremental_train_btn = self.findChild(QPushButton, f"incremental_train_btn_{lottery_key}")
            if incremental_train_btn:
                incremental_train_btn.setEnabled(True)
                incremental_train_btn.setText("🔄 增量训练 模型")
            
            # 更新模型状态
            self.update_model_status(lottery_key)
            
            lottery_name = name_path[lottery_key]['name']
            self.log_emitter.new_log.emit(f"✅ {lottery_name} 增量训练完成")
            
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 增量训练完成处理失败: {e}")

    def execute_single_algorithm(self, algorithm_method, lottery_type, num_predictions):
        """执行单个算法预测"""
        method_map = {
            "lstm_crf": self.get_lstm_crf_predictions,
            "enhanced_lstm_crf": self.get_enhanced_lstm_crf_predictions,  # 添加增强版LSTM-CRF预测方法
            "enhanced_lstm": self.get_enhanced_lstm_predictions,
            "gradient_boost": self.get_gradient_boost_predictions,
            "memory_network": self.get_memory_network_predictions,
            "weighted_expert": self.get_weighted_expert_predictions,
            "markov_chain": self.get_markov_chain_predictions
        }
        
        if algorithm_method not in method_map:
            raise ValueError(f"不支持的算法方法: {algorithm_method}")
        
        method = method_map[algorithm_method]
        return method(lottery_type, num_predictions)

    def get_recent_lottery_data(self, lottery_type, window_size=10):
        """获取最近的彩票历史数据"""
        try:
            data_path = os.path.join(name_path[lottery_type]['path'], 
                                   f"{lottery_type}_history.csv")
            
            if os.path.exists(data_path):
                if PANDAS_AVAILABLE:
                    df = pd.read_csv(data_path)
                    # 获取最近window_size期的数据
                    recent_data = df.tail(window_size)
                    return recent_data
                else:
                    self.log_emitter.new_log.emit(f"⚠️ pandas不可用，无法读取历史数据")
                    return None
            else:
                self.log_emitter.new_log.emit(f"⚠️ 历史数据文件不存在: {data_path}")
                return None
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 读取历史数据失败: {str(e)}")
            return None

    def predict_with_trained_model(self, red_model, blue_model, scaler_X, history_data, lottery_type, noise_factor=0.01):
        """使用训练好的模型进行预测"""
        try:
            if not TORCH_AVAILABLE:
                raise Exception("PyTorch不可用")
            
            # 预处理历史数据
            if lottery_type == "dlt":
                red_cols = ['红球_1', '红球_2', '红球_3', '红球_4', '红球_5']
                blue_cols = ['蓝球_1', '蓝球_2']
                red_range = (1, 35)
                blue_range = (1, 12)
                expected_red = 5
                expected_blue = 2
            else:
                red_cols = ['红球_1', '红球_2', '红球_3', '红球_4', '红球_5', '红球_6']
                blue_cols = ['蓝球_1']
                red_range = (1, 33)
                blue_range = (1, 16)
                expected_red = 6
                expected_blue = 1
            
            # 检查必要的列是否存在
            all_cols = red_cols + blue_cols
            available_cols = [col for col in all_cols if col in history_data.columns]
            
            if len(available_cols) < len(all_cols):
                # 如果列名不匹配，尝试其他可能的列名
                numeric_cols = history_data.select_dtypes(include=[np.number]).columns.tolist()
                if len(numeric_cols) >= len(all_cols):
                    features = history_data[numeric_cols[:len(all_cols)]].values
                else:
                    raise ValueError("数据列不足")
            else:
                features = history_data[available_cols].values
            
            # 数据缩放
            features_scaled = scaler_X.transform(features.reshape(-1, features.shape[-1])).reshape(features.shape)
            
            # 添加噪声
            if noise_factor > 0:
                noise = np.random.normal(0, noise_factor, features_scaled.shape)
                features_scaled = features_scaled + noise
            
            # 转换为tensor
            features_tensor = torch.tensor(features_scaled, dtype=torch.float32).unsqueeze(0)
            
            # 模型预测
            red_model.eval()
            blue_model.eval()
            
            with torch.no_grad():
                red_predictions = red_model(features_tensor)
                blue_predictions = blue_model(features_tensor)
                
                # 处理预测结果
                if isinstance(red_predictions, list) and len(red_predictions) > 0:
                    red_nums = [max(red_range[0], min(red_range[1], num + 1)) for num in red_predictions[0]]
                else:
                    # 模型预测失败，记录日志并返回空结果
                    self.log_emitter.new_log.emit("⚠️ 红球模型预测失败")
                    return [], []
                
                if isinstance(blue_predictions, list) and len(blue_predictions) > 0:
                    blue_nums = [max(blue_range[0], min(blue_range[1], num + 1)) for num in blue_predictions[0]]
                else:
                    # 模型预测失败，记录日志并返回空结果
                    self.log_emitter.new_log.emit("⚠️ 蓝球模型预测失败")
                    return [], []
                
                # 确保数字唯一性和数量正确
                red_nums = list(set(red_nums))
                while len(red_nums) < expected_red:
                    candidate = np.random.randint(red_range[0], red_range[1] + 1)
                    if candidate not in red_nums:
                        red_nums.append(candidate)
                red_nums = sorted(red_nums[:expected_red])
                
                # 蓝球处理
                blue_nums = blue_nums[:expected_blue]
                while len(blue_nums) < expected_blue:
                    blue_nums.append(np.random.randint(blue_range[0], blue_range[1] + 1))
                blue_nums = sorted(blue_nums)
                
                return red_nums, blue_nums
                
        except Exception as e:
            self.log_emitter.new_log.emit(f"⚠️ 模型预测失败: {str(e)}")
            # 模型不可用时，返回空结果
            return [], []

    def get_deterministic_prediction(self, lottery_type):
        """确定性预测：固定种子的随机预测，确保每次运行结果相同"""
        np.random.seed(42)  # 固定种子确保可重复性
        
        if lottery_type == "dlt":
            red_nums = sorted(np.random.choice(range(1, 36), 5, replace=False))
            blue_nums = sorted(np.random.choice(range(1, 13), 2, replace=False))
        else:
            red_nums = sorted(np.random.choice(range(1, 34), 6, replace=False))
            blue_nums = [np.random.randint(1, 17)]
        
        return red_nums.tolist(), blue_nums
    
    def load_models_with_cache(self, lottery_type):
        """带缓存机制的模型加载"""
        import os
        import time
        
        # 检查缓存是否存在
        cached_data = self.model_cache.get(lottery_type, {})
        cached_models = cached_data.get('models')
        cached_timestamp = cached_data.get('timestamp')
        
        # 检查模型文件是否存在
        model_path = os.path.join(name_path[lottery_type]['path'], name_path[lottery_type]['model_file'])
        
        if not os.path.exists(model_path):
            self.log_emitter.new_log.emit(f"⚠️ {name_path[lottery_type]['name']} 模型文件不存在: {model_path}")
            self.log_emitter.new_log.emit("💡 解决方案:")
            self.log_emitter.new_log.emit("   1. 确保已运行数据采集获取历史数据")
            self.log_emitter.new_log.emit("   2. 运行对应的模型训练脚本:")
            if lottery_type == "dlt":
                self.log_emitter.new_log.emit("      - 基础模型: python scripts/dlt/train_dlt_model.py")
                self.log_emitter.new_log.emit("      - 增强版模型: python scripts/dlt/train_enhanced_lstm_crf.py")
            elif lottery_type == "ssq":
                self.log_emitter.new_log.emit("      - 基础模型: python scripts/ssq/train_ssq_model.py")
                self.log_emitter.new_log.emit("      - 增强版模型: python scripts/ssq/train_enhanced_lstm_crf.py")
            elif lottery_type == "plw":
                self.log_emitter.new_log.emit("      - 基础模型: python scripts/plw/train_plw_model.py")
                self.log_emitter.new_log.emit("      - 序列模型: python scripts/plw/train_plw_sequence_lstm.py")
                self.log_emitter.new_log.emit("      - 增强版模型: python scripts/plw/train_enhanced_lstm_crf.py")
            elif lottery_type == "kl8":
                self.log_emitter.new_log.emit("      - 基础模型: python scripts/kl8/train_kl8_model.py")
            raise FileNotFoundError(f"模型文件不存在：{model_path}")
        
        # 获取模型文件的修改时间
        model_mtime = os.path.getmtime(model_path)
        
        # 检查是否需要重新加载（缓存不存在或模型文件已更新）
        if cached_models is None or cached_timestamp is None or model_mtime > cached_timestamp:
            self.log_emitter.new_log.emit(f"🔄 正在加载 {name_path[lottery_type]['name']} 模型...")
            
            # 排列5特殊处理
            if lottery_type == "plw":
                # 排列5只有一个模型，返回格式不同
                plw_model, _, scaler_X = load_resources_pytorch(lottery_type)
                
                # 更新缓存（排列5特殊缓存格式）
                self.model_cache[lottery_type] = {
                    'models': (plw_model, None, scaler_X),  # blue_model为None
                    'timestamp': time.time()
                }
                
                self.log_emitter.new_log.emit(f"✅ {name_path[lottery_type]['name']} 模型加载成功")
                return plw_model, None, scaler_X
            if lottery_type == "fc3d":
                # 排列5只有一个模型，返回格式不同
                plw_model, _, scaler_X = load_resources_pytorch(lottery_type)
                
                # 更新缓存（排列5特殊缓存格式）
                self.model_cache[lottery_type] = {
                    'models': (plw_model, None, scaler_X),  # blue_model为None
                    'timestamp': time.time()
                }
                
                self.log_emitter.new_log.emit(f"✅ {name_path[lottery_type]['name']} 模型加载成功")
                return plw_model, None, scaler_X
            else:
                # 大乐透和双色球的原有逻辑
                red_model, blue_model, scaler_X = load_resources_pytorch(lottery_type)
                
                # 更新缓存
                self.model_cache[lottery_type] = {
                    'models': (red_model, blue_model, scaler_X),
                    'timestamp': time.time()
                }
                
                self.log_emitter.new_log.emit(f"✅ {name_path[lottery_type]['name']} 模型加载成功")
                return red_model, blue_model, scaler_X
        else:
            # 使用缓存的模型
            self.log_emitter.new_log.emit(f"📋 使用缓存的 {name_path[lottery_type]['name']} 模型")
            return cached_models

    def analyze_prediction_consistency(self, all_predictions, lottery_type):
        """分析预测结果的一致性"""
        analysis_text = "\n🔍 一致性分析:\n"
        
        successful_predictions = []
        
        # 对于排列5，确定性算法只需要统计一次预测结果
        if lottery_type == "plw":
            processed_algorithms = set()  # 记录已处理的算法
            deterministic_algorithms = {"🤖 LSTM-CRF 经典模式", "🔥 序列LSTM 增强模式"}  # 确定性算法
            
            for algo_name, result in all_predictions.items():
                if result['success'] and result['predictions']:
                    # 如果是确定性算法
                    if algo_name in deterministic_algorithms:
                        # 只添加一次预测结果
                        if algo_name not in processed_algorithms and len(result['predictions']) > 0:
                            successful_predictions.append(result['predictions'][0])  # 只添加第一组预测
                            processed_algorithms.add(algo_name)
                    else:
                        # 非确定性算法添加所有预测结果
                        for pred in result['predictions']:
                            successful_predictions.append(pred)
        else:
            # 非排列5情况，添加所有预测结果
            for algo_name, result in all_predictions.items():
                if result['success'] and result['predictions']:
                    for pred in result['predictions']:
                        successful_predictions.append(pred)
        
        if len(successful_predictions) < 2:
            analysis_text += "  ⚠️ 有效预测结果太少，无法进行一致性分析\n"
            return analysis_text
        
        # 统计最常出现的数字
        if lottery_type == "plw":
            # 对于排列5，按位置分别统计频率
            position_frequencies = [{} for _ in range(5)]  # 个位到万位，共5个位置
            
            for pred in successful_predictions:
                red_nums = pred['red']
                # 确保red_nums有5个数字
                if len(red_nums) == 5:
                    for pos in range(5):
                        num = red_nums[pos]
                        position_frequencies[pos][num] = position_frequencies[pos].get(num, 0) + 1
        else:
            # 非排列5情况，统一统计
            red_frequency = {}
            blue_frequency = {}
            
            for pred in successful_predictions:
                for red_num in pred['red']:
                    red_frequency[red_num] = red_frequency.get(red_num, 0) + 1
                
                # 排列5没有蓝球，跳过蓝球统计
                if lottery_type != "plw":
                    for blue_num in pred['blue']:
                        blue_frequency[blue_num] = blue_frequency.get(blue_num, 0) + 1
        
        # 找出高频数字
        min_appearances = max(2, len(successful_predictions) // 3)
        
        if lottery_type == "plw":
            # 排列5按位置分别找出高频数字
            analysis_text += f"  🔥 高频数字 (出现{min_appearances}次以上):\n"
            position_names = ["万位", "千位", "百位", "十位", "个位"]
            for pos in range(5):
                # 按频率排序，显示前3个最频繁的数字
                sorted_freq = sorted(position_frequencies[pos].items(), key=lambda x: x[1], reverse=True)
                hot_numbers = [(num, freq) for num, freq in sorted_freq if freq >= min_appearances]
                if hot_numbers:
                    # 只显示频率最高的前3个数字
                    top_numbers = hot_numbers[:3]
                    numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_numbers])
                    analysis_text += f"    {position_names[pos]}: {numbers_str}\n"
                else:
                    # 如果没有达到最小出现次数的数字，显示频率最高的1-2个数字
                    if sorted_freq:
                        top_numbers = sorted_freq[:2]
                        numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_numbers])
                        analysis_text += f"    {position_names[pos]}: {numbers_str}\n"
        else:
            # 非排列5情况，统一找出高频数字
            hot_red_numbers = [num for num, freq in red_frequency.items() if freq >= min_appearances]
            hot_blue_numbers = [num for num, freq in blue_frequency.items() if freq >= min_appearances]
            
            if hot_red_numbers or hot_blue_numbers:
                analysis_text += f"  🔥 高频数字 (出现{min_appearances}次以上):\n"
                if hot_red_numbers:
                    if lottery_type == "dlt":
                        # 按频率排序显示前区高频数字
                        sorted_red = sorted(red_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_red = [(num, freq) for num, freq in sorted_red if freq >= min_appearances][:5]
                        if top_red:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_red])
                            analysis_text += f"    前区高频: {numbers_str}\n"
                    elif lottery_type == "plw":
                        sorted_red = sorted(red_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_red = [(num, freq) for num, freq in sorted_red if freq >= min_appearances][:5]
                        if top_red:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_red])
                            analysis_text += f"    数字高频: {numbers_str}\n"
                    elif lottery_type == "kl8":
                        # 快乐8按频率排序显示高频数字
                        sorted_red = sorted(red_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_red = [(num, freq) for num, freq in sorted_red if freq >= min_appearances][:10]
                        if top_red:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_red])
                            analysis_text += f"    红球高频: {numbers_str}\n"
                    else:  # ssq
                        # 按频率排序显示红球高频数字
                        sorted_red = sorted(red_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_red = [(num, freq) for num, freq in sorted_red if freq >= min_appearances][:6]
                        if top_red:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_red])
                            analysis_text += f"    红球高频: {numbers_str}\n"
                
                # 只在非排列5的情况下显示蓝球高频
                if hot_blue_numbers and lottery_type != "plw":
                    if lottery_type == "dlt":
                        # 按频率排序显示后区高频数字
                        sorted_blue = sorted(blue_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_blue = [(num, freq) for num, freq in sorted_blue if freq >= min_appearances][:3]
                        if top_blue:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_blue])
                            analysis_text += f"    后区高频: {numbers_str}\n"
                    else:  # ssq
                        # 按频率排序显示蓝球高频数字
                        sorted_blue = sorted(blue_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_blue = [(num, freq) for num, freq in sorted_blue if freq >= min_appearances][:3]
                        if top_blue:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_blue])
                            analysis_text += f"    蓝球高频: {numbers_str}\n"
            else:
                # 如果没有达到最小出现次数的数字，显示总体频率最高的几个数字
                analysis_text += f"  ℹ️ 各算法预测结果差异较大，显示总体频率最高的数字:\n"
                if lottery_type != "plw":
                    # 显示红球频率最高的数字
                    sorted_red = sorted(red_frequency.items(), key=lambda x: x[1], reverse=True)
                    top_red = sorted_red[:3]  # 显示前3个
                    if top_red:
                        numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_red])
                        if lottery_type == "dlt":
                            analysis_text += f"    前区高频: {numbers_str}\n"
                        elif lottery_type == "plw":
                            analysis_text += f"    数字高频: {numbers_str}\n"
                        elif lottery_type == "kl8":
                            analysis_text += f"    红球高频: {numbers_str}\n"
                        else:  # ssq
                            analysis_text += f"    红球高频: {numbers_str}\n"
                    
                    # 显示蓝球频率最高的数字（非排列5）
                    if lottery_type != "plw":
                        sorted_blue = sorted(blue_frequency.items(), key=lambda x: x[1], reverse=True)
                        top_blue = sorted_blue[:2]  # 显示前2个
                        if top_blue:
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_blue])
                            if lottery_type == "dlt":
                                analysis_text += f"    后区高频: {numbers_str}\n"
                            else:  # ssq
                                analysis_text += f"    蓝球高频: {numbers_str}\n"
                else:
                    # 排列5按位置显示频率最高的数字
                    analysis_text += f"  🔥 各位置频率最高的数字:\n"
                    position_names = ["万位", "千位", "百位", "十位", "个位"]
                    for pos in range(5):
                        sorted_freq = sorted(position_frequencies[pos].items(), key=lambda x: x[1], reverse=True)
                        if sorted_freq:
                            top_numbers = sorted_freq[:2]  # 显示前2个
                            numbers_str = ", ".join([f"{num}({freq}次)" for num, freq in top_numbers])
                            analysis_text += f"    {position_names[pos]}: {numbers_str}\n"
        
        analysis_text += f"  📋 样本数量: {len(successful_predictions)} 组预测结果\n"
        
        # 添加高权限的统计排序功能
        analysis_text += "\n  🎯 高权限统计排序:\n"
        
        # 合并所有数字频率统计（不区分红球蓝球）
        all_frequency = {}
        if lottery_type == "plw":
            # 排列5按位置分别统计
            for pos_freq in position_frequencies:
                for num, freq in pos_freq.items():
                    all_frequency[num] = all_frequency.get(num, 0) + freq
        elif lottery_type == "kl8":
            # 快乐8使用红球频率统计
            all_frequency = red_frequency
        else:
            # 其他彩票类型合并红球和蓝球频率
            all_frequency = red_frequency.copy()
            for num, freq in blue_frequency.items():
                all_frequency[num] = all_frequency.get(num, 0) + freq
        
        # 按频率排序，显示前15个最频繁的数字
        sorted_all = sorted(all_frequency.items(), key=lambda x: x[1], reverse=True)
        top_all = sorted_all[:15]
        
        if top_all:
            # 按出现次数分组显示
            freq_groups = {}
            for num, freq in top_all:
                if freq not in freq_groups:
                    freq_groups[freq] = []
                freq_groups[freq].append(num)
            
            # 按频率从高到低显示
            for freq in sorted(freq_groups.keys(), reverse=True):
                numbers = sorted(freq_groups[freq])
                numbers_str = ", ".join([f"{num}" for num in numbers])
                analysis_text += f"    出现{freq}次: {numbers_str}\n"
        else:
            analysis_text += "    无高频数字统计\n"
        
        return analysis_text

    def generate_comprehensive_multi_algorithm_prediction(self):
        """统一多算法对比预测 - 同时展示所有算法的预测结果（带优先级排序）"""
        selected_index = self.lottery_combo.currentIndex()
        selected_key = list(name_path.keys())[selected_index]
        lottery_type = selected_key
        lottery_name = name_path[selected_key]['name']
        num_predictions = self.prediction_spin.value()
        
        self.log_emitter.new_log.emit(f"🎆 启动统一多算法对比预测系统...")
        
        # 定义所有可用的算法
        algorithms = [
            {"name": "💎 LSTM-CRF 经典模式增强版", "method": "enhanced_lstm_crf"},  # 确保这行存在
            {"name": "🤖 LSTM-CRF 经典模式", "method": "lstm_crf"},
            {"name": "🔥 序列LSTM 增强模式", "method": "enhanced_lstm"},
            {"name": "📈 梯度提升 特征模式", "method": "gradient_boost"},
            {"name": " 模式记忆 网络模式", "method": "memory_network"},
            {"name": "⚙️ 权重维度 专家模式", "method": "weighted_expert"},
            {"name": "🔗 马尔可夫链 模型", "method": "markov_chain"}
        ]
        
        # 根据优先级排序算法
        sorted_algorithms = self.sort_algorithms_by_priority(algorithms)
        
        all_predictions = {}
        successful_count = 0
        
        # 依次执行每个算法（按优先级顺序）
        for algo_info in sorted_algorithms:
            algo_name = algo_info["name"]
            algo_method = algo_info["method"]
            
            self.log_emitter.new_log.emit(f"💻 正在执行: {algo_name}")
            
            try:
                predictions = self.execute_single_algorithm(algo_method, lottery_type, num_predictions)
                all_predictions[algo_name] = {
                    'success': True,
                    'predictions': predictions,
                    'method': algo_method
                }
                successful_count += 1
                self.log_emitter.new_log.emit(f"✅ {algo_name} 预测完成")
            except Exception as e:
                all_predictions[algo_name] = {
                    'success': False,
                    'error': str(e),
                    'method': algo_method
                }
                self.log_emitter.new_log.emit(f"❌ {algo_name} 预测失败: {str(e)}")
        
        # 生成统一展示结果
        if successful_count > 0:
            result_text = f"🎆 统一多算法对比预测 - {lottery_name}\n"
            result_text += "=" * 60 + "\n"
            result_text += f"🎯 预测数量: {num_predictions} 组 | 成功算法: {successful_count}/{len(sorted_algorithms)}\n\n"
            
            # 显示算法优先级排序
            result_text += "算法优先级排序:\n"
            for i, algo in enumerate(sorted_algorithms, 1):
                priority = self.algorithm_priorities.get(algo['method'], 999)
                result_text += f"  {i}. {algo['name']} (优先级: {priority})\n"
            result_text += "\n"
            
            # 显示每个算法的结果
            for algo_name, result in all_predictions.items():
                # 获取算法优先级
                algo_priority = 999
                for algo in sorted_algorithms:
                    if algo['name'] == algo_name:
                        algo_priority = self.algorithm_priorities.get(algo['method'], 999)
                        break
                
                result_text += f"{algo_name} (优先级: {algo_priority}):\n"
                
                if result['success']:
                    for i, pred in enumerate(result['predictions'], 1):
                        red_balls = pred['red']
                        blue_balls = pred['blue']
                        
                        # 根据彩票类型调整显示格式
                        if lottery_type == "dlt":
                            result_text += f"  预测 {i}: 前区 {red_balls} | 后区 {blue_balls}\n"
                        elif lottery_type == "plw":
                            # 排列5只有5个数字，没有蓝球
                            result_text += f"  预测 {i}: 数字 {red_balls}\n"
                        elif lottery_type == "fc3d":
                            # 3D彩票只有3个数字，没有蓝球
                            result_text += f"  预测 {i}: 数字 {red_balls}\n"
                        elif lottery_type == "kl8":
                            # 快乐8有20个数字，没有蓝球
                            result_text += f"  预测 {i}: 号码 {red_balls}\n"
                        else:  # ssq 双色球
                            result_text += f"  预测 {i}: 红球 {red_balls} | 蓝球 {blue_balls}\n"

                else:
                    result_text += f"  ⚠️ 执行失败: {result.get('error', '未知错误')}\n"
                
                result_text += "\n"
            
            # 添加一致性分析
            consistency_analysis = self.analyze_prediction_consistency(all_predictions, lottery_type)
            result_text += consistency_analysis
            
            # 添加算法成功率统计
            success_rate = (successful_count / len(algorithms)) * 100
            result_text += f"\n📈 算法成功率: {success_rate:.1f}% ({successful_count}/{len(algorithms)})\n"
            
            from datetime import datetime
            result_text += f"🕰️ 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
            
            self.update_current_tab_result(result_text)
            self.log_emitter.new_log.emit(f"🎉 统一多算法预测完成！成功率: {success_rate:.1f}%")
        else:
            error_text = "⚠️ 统一多算法预测失败\n\n"
            error_text += "🚫 所有算法都执行失败，请检查系统设置:\n"
            for algo_name, result in all_predictions.items():
                if not result['success']:
                    error_text += f"  ❌ {algo_name}: {result.get('error', '未知错误')}\n"
            
            self.update_current_tab_result(error_text)
            self.log_emitter.new_log.emit("❌ 统一多算法预测完全失败")


# ---------------- 主程序入口 ----------------
def main():
    app = QApplication(sys.argv)
    main_window = LotteryPredictorApp()
    main_window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()